├── test ├── scenarios │ ├── literal │ │ ├── code.adoc │ │ ├── asterisks.adoc │ │ ├── asterisks.html │ │ ├── attributes.adoc │ │ ├── attributes.html │ │ ├── code.html │ │ ├── intrinsic_attributes.html │ │ ├── intrinsic_attributes.adoc │ │ ├── command-prompt.adoc │ │ └── command-prompt.html │ ├── code │ │ ├── intrinsic_attributes.adoc │ │ ├── nested.adoc │ │ ├── intrinsic_attributes.html │ │ ├── nested.html │ │ ├── markup.adoc │ │ └── markup.html │ ├── passthrough │ │ ├── verbatim.html │ │ ├── escaped.adoc │ │ ├── verbatim.adoc │ │ ├── escaped.html │ │ ├── inline_macro.html │ │ ├── inline_triple_plus.html │ │ ├── inline_triple_plus.adoc │ │ ├── inline_macro_default_subs.html │ │ ├── inline_macro.adoc │ │ └── inline_macro_default_subs.adoc │ ├── emphasis │ │ ├── constrained.adoc │ │ ├── constrained.html │ │ ├── strong.adoc │ │ ├── strong.html │ │ ├── unconstrained.adoc │ │ └── unconstrained.html │ ├── keyboard │ │ ├── plus.adoc │ │ ├── single.adoc │ │ ├── combo-duo.adoc │ │ ├── single.html │ │ ├── backslash.adoc │ │ ├── backslash.html │ │ ├── combo-trio.adoc │ │ ├── escaped-closed-bracket.adoc │ │ ├── plus.html │ │ ├── combo-duo.html │ │ ├── escaped-closed-bracket.html │ │ └── combo-trio.html │ ├── smart_quotes │ │ ├── apostrophe.adoc │ │ ├── double.adoc │ │ ├── double.html │ │ ├── single.adoc │ │ ├── single.html │ │ └── apostrophe.html │ ├── email │ │ ├── link.adoc │ │ ├── escaped.adoc │ │ ├── escaped.html │ │ ├── implicit.adoc │ │ ├── link.html │ │ ├── subject.adoc │ │ ├── tag.adoc │ │ ├── subject.html │ │ ├── body.adoc │ │ ├── implicit.html │ │ ├── explicit.adoc │ │ ├── tag.html │ │ ├── body.html │ │ ├── explicit.html │ │ ├── second-level-domain.adoc │ │ └── second-level-domain.html │ ├── menu │ │ ├── submenu.adoc │ │ ├── target.adoc │ │ ├── target.html │ │ ├── subitem.adoc │ │ ├── submenu.html │ │ └── subitem.html │ ├── button │ │ ├── ok.adoc │ │ ├── open.adoc │ │ ├── ok.html │ │ └── open.html │ ├── strong │ │ ├── nested-emphasis.adoc │ │ ├── constrained.adoc │ │ ├── nested-emphasis.html │ │ ├── constrained.html │ │ ├── unconstrained.adoc │ │ └── unconstrained.html │ ├── link │ │ ├── bare.adoc │ │ ├── quoted-label.adoc │ │ ├── url-special-characters.adoc │ │ ├── quoted-label.html │ │ ├── url-special-characters.html │ │ ├── window-blank.adoc │ │ ├── irc.adoc │ │ ├── window-blank.html │ │ ├── bare.html │ │ ├── label.adoc │ │ ├── role.adoc │ │ ├── url-special-characters-encoded.adoc │ │ ├── window-blank-shorthand.adoc │ │ ├── role.html │ │ ├── url-special-characters-encoded.html │ │ ├── label.html │ │ ├── window-blank-shorthand.html │ │ └── irc.html │ ├── image │ │ ├── title.adoc │ │ ├── name-title.adoc │ │ ├── positioning-role.adoc │ │ ├── title.html │ │ ├── name-title.html │ │ └── positioning-role.html │ └── substitution │ │ ├── markup.adoc │ │ └── markup.html ├── test_helper.rb ├── scenarios_test.rb ├── html5_converter.rb ├── quoted_text_test.rb └── inline_parser_test.rb ├── Gemfile ├── lib └── asciidoctor │ ├── inline_parser.rb │ └── inline_parser │ ├── version.rb │ ├── asciidoctor_btn_grammar.treetop │ ├── asciidoctor_kbd_grammar.treetop │ ├── asciidoctor_menu_grammar.treetop │ ├── asciidoctor_passthrough_grammar.treetop │ ├── asciidoctor_image_grammar.treetop │ ├── asciidoctor_email_grammar.treetop │ ├── parser.rb │ ├── asciidoctor_link_grammar.treetop │ ├── asciidoctor_btn_grammar.rb │ ├── asciidoctor_kbd_grammar.rb │ ├── asciidoctor_menu_grammar.rb │ ├── asciidoctor_grammar.treetop │ ├── subs.rb │ ├── asciidoctor_image_grammar.rb │ ├── asciidoctor_email_grammar.rb │ ├── asciidoctor_passthrough_grammar.rb │ ├── node_extensions.rb │ └── asciidoctor_link_grammar.rb ├── .gitignore ├── .travis.yml ├── .editorconfig ├── .rubocop.yml ├── TODO.adoc ├── README.adoc ├── asciidoctor-inline-parser.gemspec └── Rakefile /test/scenarios/literal/code.adoc: -------------------------------------------------------------------------------- 1 | `+code;+` -------------------------------------------------------------------------------- /test/scenarios/literal/asterisks.adoc: -------------------------------------------------------------------------------- 1 | +*plain*+ -------------------------------------------------------------------------------- /test/scenarios/literal/asterisks.html: -------------------------------------------------------------------------------- 1 | *plain* -------------------------------------------------------------------------------- /test/scenarios/literal/attributes.adoc: -------------------------------------------------------------------------------- 1 | +{plus}+ -------------------------------------------------------------------------------- /test/scenarios/literal/attributes.html: -------------------------------------------------------------------------------- 1 | {plus} -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec 3 | -------------------------------------------------------------------------------- /test/scenarios/code/intrinsic_attributes.adoc: -------------------------------------------------------------------------------- 1 | `{cpp}` -------------------------------------------------------------------------------- /test/scenarios/literal/code.html: -------------------------------------------------------------------------------- 1 | code; -------------------------------------------------------------------------------- /test/scenarios/literal/intrinsic_attributes.html: -------------------------------------------------------------------------------- 1 | {cpp} -------------------------------------------------------------------------------- /test/scenarios/literal/intrinsic_attributes.adoc: -------------------------------------------------------------------------------- 1 | +{cpp}+ -------------------------------------------------------------------------------- /test/scenarios/passthrough/verbatim.html: -------------------------------------------------------------------------------- 1 | <{backend}> -------------------------------------------------------------------------------- /test/scenarios/emphasis/constrained.adoc: -------------------------------------------------------------------------------- 1 | This is _so_ _easy_. -------------------------------------------------------------------------------- /test/scenarios/keyboard/plus.adoc: -------------------------------------------------------------------------------- 1 | Increase zoom: kbd:[Ctrl + +] -------------------------------------------------------------------------------- /test/scenarios/keyboard/single.adoc: -------------------------------------------------------------------------------- 1 | Toggle fullscreen: kbd:[F11] -------------------------------------------------------------------------------- /test/scenarios/smart_quotes/apostrophe.adoc: -------------------------------------------------------------------------------- 1 | That's all folks! -------------------------------------------------------------------------------- /test/scenarios/smart_quotes/double.adoc: -------------------------------------------------------------------------------- 1 | "`double curved quotes`" -------------------------------------------------------------------------------- /test/scenarios/smart_quotes/double.html: -------------------------------------------------------------------------------- 1 | “double curved quotes” -------------------------------------------------------------------------------- /test/scenarios/smart_quotes/single.adoc: -------------------------------------------------------------------------------- 1 | '`single curved quotes`' -------------------------------------------------------------------------------- /test/scenarios/smart_quotes/single.html: -------------------------------------------------------------------------------- 1 | ‘single curved quotes’ -------------------------------------------------------------------------------- /test/scenarios/code/nested.adoc: -------------------------------------------------------------------------------- 1 | This code is *really `important`*. -------------------------------------------------------------------------------- /test/scenarios/keyboard/combo-duo.adoc: -------------------------------------------------------------------------------- 1 | Open a new tab: kbd:[Ctrl+T] -------------------------------------------------------------------------------- /test/scenarios/keyboard/single.html: -------------------------------------------------------------------------------- 1 | Toggle fullscreen: F11 -------------------------------------------------------------------------------- /test/scenarios/passthrough/escaped.adoc: -------------------------------------------------------------------------------- 1 | use the `\pass:c[]` macro -------------------------------------------------------------------------------- /test/scenarios/passthrough/verbatim.adoc: -------------------------------------------------------------------------------- 1 | pass:verbatim[<{backend}>] -------------------------------------------------------------------------------- /test/scenarios/smart_quotes/apostrophe.html: -------------------------------------------------------------------------------- 1 | That’s all folks! -------------------------------------------------------------------------------- /test/scenarios/email/link.adoc: -------------------------------------------------------------------------------- 1 | mailto:doc.writer@asciidoc.org[Doc Writer] -------------------------------------------------------------------------------- /test/scenarios/keyboard/backslash.adoc: -------------------------------------------------------------------------------- 1 | Used to escape characters: kbd:[\ ] -------------------------------------------------------------------------------- /test/scenarios/menu/submenu.adoc: -------------------------------------------------------------------------------- 1 | To save the file, select menu:File[Save]. -------------------------------------------------------------------------------- /test/scenarios/button/ok.adoc: -------------------------------------------------------------------------------- 1 | Press the btn:[OK] button when you are finished. -------------------------------------------------------------------------------- /test/scenarios/code/intrinsic_attributes.html: -------------------------------------------------------------------------------- 1 | C++ 2 | -------------------------------------------------------------------------------- /test/scenarios/emphasis/constrained.html: -------------------------------------------------------------------------------- 1 | This is so easy. -------------------------------------------------------------------------------- /test/scenarios/keyboard/backslash.html: -------------------------------------------------------------------------------- 1 | Used to escape characters: \ -------------------------------------------------------------------------------- /test/scenarios/literal/command-prompt.adoc: -------------------------------------------------------------------------------- 1 | $ docker -it --run node:8-alpine -------------------------------------------------------------------------------- /test/scenarios/menu/target.adoc: -------------------------------------------------------------------------------- 1 | Editing tools can be found in the menu:Edit[]. -------------------------------------------------------------------------------- /test/scenarios/passthrough/escaped.html: -------------------------------------------------------------------------------- 1 | use the pass:c[] macro -------------------------------------------------------------------------------- /test/scenarios/passthrough/inline_macro.html: -------------------------------------------------------------------------------- 1 | <code>['code'\]</code> -------------------------------------------------------------------------------- /test/scenarios/passthrough/inline_triple_plus.html: -------------------------------------------------------------------------------- 1 | inline code -------------------------------------------------------------------------------- /test/scenarios/email/escaped.adoc: -------------------------------------------------------------------------------- 1 | Please don't email me at \doc.writer@asciidoc.org! -------------------------------------------------------------------------------- /test/scenarios/email/escaped.html: -------------------------------------------------------------------------------- 1 | Please don't email me at doc.writer@asciidoc.org! -------------------------------------------------------------------------------- /test/scenarios/keyboard/combo-trio.adoc: -------------------------------------------------------------------------------- 1 | New incognito window: kbd:[Ctrl+Shift+N] -------------------------------------------------------------------------------- /test/scenarios/keyboard/escaped-closed-bracket.adoc: -------------------------------------------------------------------------------- 1 | Jump to keyword: kbd:[Ctrl+\]] -------------------------------------------------------------------------------- /test/scenarios/passthrough/inline_triple_plus.adoc: -------------------------------------------------------------------------------- 1 | +++inline code+++ -------------------------------------------------------------------------------- /test/scenarios/strong/nested-emphasis.adoc: -------------------------------------------------------------------------------- 1 | *_This sentence has reached max volume._* -------------------------------------------------------------------------------- /test/scenarios/button/open.adoc: -------------------------------------------------------------------------------- 1 | Select a file in the file navigator and click btn:[Open]. -------------------------------------------------------------------------------- /test/scenarios/email/implicit.adoc: -------------------------------------------------------------------------------- 1 | Here is my email address: doc.writer@asciidoc.org. -------------------------------------------------------------------------------- /test/scenarios/email/link.html: -------------------------------------------------------------------------------- 1 | Doc Writer -------------------------------------------------------------------------------- /test/scenarios/link/bare.adoc: -------------------------------------------------------------------------------- 1 | The AsciiDoc project is located at http://asciidoc.org. -------------------------------------------------------------------------------- /test/scenarios/strong/constrained.adoc: -------------------------------------------------------------------------------- 1 | You *really* need to check out this *awesomeness*. -------------------------------------------------------------------------------- /test/scenarios/button/ok.html: -------------------------------------------------------------------------------- 1 | Press the OK button when you are finished. -------------------------------------------------------------------------------- /test/scenarios/code/nested.html: -------------------------------------------------------------------------------- 1 | This code is really important. -------------------------------------------------------------------------------- /test/scenarios/email/subject.adoc: -------------------------------------------------------------------------------- 1 | mailto:doc.writer@asciidoc.org[Doc Writer, Pull request] -------------------------------------------------------------------------------- /test/scenarios/menu/target.html: -------------------------------------------------------------------------------- 1 | Editing tools can be found in the Edit. -------------------------------------------------------------------------------- /test/scenarios/passthrough/inline_macro_default_subs.html: -------------------------------------------------------------------------------- 1 | underline me is underlined. -------------------------------------------------------------------------------- /test/scenarios/image/title.adoc: -------------------------------------------------------------------------------- 1 | Click image:icons/pause.png[title="Pause"] when you need a break. -------------------------------------------------------------------------------- /test/scenarios/passthrough/inline_macro.adoc: -------------------------------------------------------------------------------- 1 | pass:specialcharacters,quotes[['code'\\]] -------------------------------------------------------------------------------- /test/scenarios/passthrough/inline_macro_default_subs.adoc: -------------------------------------------------------------------------------- 1 | pass:[underline me] is underlined. -------------------------------------------------------------------------------- /test/scenarios/email/tag.adoc: -------------------------------------------------------------------------------- 1 | You can support this project by becoming a sponsor: hello+donation@4fs.no -------------------------------------------------------------------------------- /test/scenarios/emphasis/strong.adoc: -------------------------------------------------------------------------------- 1 | Using *_bold and italic_* is the strongest way to emphasize text. -------------------------------------------------------------------------------- /test/scenarios/substitution/markup.adoc: -------------------------------------------------------------------------------- 1 | All your inline markup belongs to us! -------------------------------------------------------------------------------- /test/scenarios/button/open.html: -------------------------------------------------------------------------------- 1 | Select a file in the file navigator and click Open. -------------------------------------------------------------------------------- /test/scenarios/image/name-title.adoc: -------------------------------------------------------------------------------- 1 | Click image:icons/play.png[Play, title="Play"] to get the party started. -------------------------------------------------------------------------------- /test/scenarios/keyboard/plus.html: -------------------------------------------------------------------------------- 1 | Increase zoom: Ctrl++ -------------------------------------------------------------------------------- /test/scenarios/link/quoted-label.adoc: -------------------------------------------------------------------------------- 1 | https://example.org["Google, Yahoo, Bing"]. Pick your search engine. -------------------------------------------------------------------------------- /test/scenarios/strong/nested-emphasis.html: -------------------------------------------------------------------------------- 1 | This sentence has reached max volume. -------------------------------------------------------------------------------- /test/scenarios/email/subject.html: -------------------------------------------------------------------------------- 1 | Doc Writer -------------------------------------------------------------------------------- /test/scenarios/image/positioning-role.adoc: -------------------------------------------------------------------------------- 1 | image:sunset.jpg[Sunset,150,150,role="right"] What a beautiful sunset! -------------------------------------------------------------------------------- /test/scenarios/keyboard/combo-duo.html: -------------------------------------------------------------------------------- 1 | Open a new tab: Ctrl+T -------------------------------------------------------------------------------- /test/scenarios/link/url-special-characters.adoc: -------------------------------------------------------------------------------- 1 | link:++https://example.org/?q=[a b]++[URL with special characters] -------------------------------------------------------------------------------- /test/scenarios/menu/subitem.adoc: -------------------------------------------------------------------------------- 1 | Select menu:View[Zoom > Reset] to reset the zoom level to the default setting. -------------------------------------------------------------------------------- /test/scenarios/email/body.adoc: -------------------------------------------------------------------------------- 1 | mailto:doc.writer@asciidoc.org[Doc Writer, Pull request, Please accept my pull request] -------------------------------------------------------------------------------- /test/scenarios/link/quoted-label.html: -------------------------------------------------------------------------------- 1 | Google, Yahoo, Bing. Pick your search engine. -------------------------------------------------------------------------------- /test/scenarios/link/url-special-characters.html: -------------------------------------------------------------------------------- 1 | URL with special characters -------------------------------------------------------------------------------- /test/scenarios/link/window-blank.adoc: -------------------------------------------------------------------------------- 1 | Join us on https://gitter.im/asciidoctor/asciidoctor[Gitter, window="_blank"]. -------------------------------------------------------------------------------- /test/scenarios/strong/constrained.html: -------------------------------------------------------------------------------- 1 | You really need to check out this awesomeness. -------------------------------------------------------------------------------- /lib/asciidoctor/inline_parser.rb: -------------------------------------------------------------------------------- 1 | require_relative 'inline_parser/version' 2 | require_relative 'inline_parser/parser' 3 | -------------------------------------------------------------------------------- /test/scenarios/emphasis/strong.html: -------------------------------------------------------------------------------- 1 | Using bold and italic is the strongest way to emphasize text. -------------------------------------------------------------------------------- /test/scenarios/keyboard/escaped-closed-bracket.html: -------------------------------------------------------------------------------- 1 | Jump to keyword: Ctrl+] -------------------------------------------------------------------------------- /test/scenarios/link/irc.adoc: -------------------------------------------------------------------------------- 1 | IRC (Internet Relay Chat) is a chat service used in Fedora community: irc://irc.freenode.org/#fedora -------------------------------------------------------------------------------- /test/scenarios/link/window-blank.html: -------------------------------------------------------------------------------- 1 | Join us on Gitter. -------------------------------------------------------------------------------- /test/scenarios/substitution/markup.html: -------------------------------------------------------------------------------- 1 | All your <em>inline</em> markup belongs to <strong>us</strong>! -------------------------------------------------------------------------------- /test/scenarios/email/implicit.html: -------------------------------------------------------------------------------- 1 | Here is my email address: doc.writer@asciidoc.org. -------------------------------------------------------------------------------- /test/scenarios/link/bare.html: -------------------------------------------------------------------------------- 1 | The AsciiDoc project is located at http://asciidoc.org. -------------------------------------------------------------------------------- /test/scenarios/link/label.adoc: -------------------------------------------------------------------------------- 1 | https://asciidoctor.org/[Asciidoctor] is a fast, open source text processor and publishing toolchain. -------------------------------------------------------------------------------- /test/scenarios/link/role.adoc: -------------------------------------------------------------------------------- 1 | Join us on https://gitter.im/asciidoctor/asciidoctor[Gitter to discuss Asciidoctor, role="external"]! -------------------------------------------------------------------------------- /test/scenarios/link/url-special-characters-encoded.adoc: -------------------------------------------------------------------------------- 1 | link:https://example.org/?q=%5Ba%20b%5D[URL with special characters encoded] -------------------------------------------------------------------------------- /test/scenarios/link/window-blank-shorthand.adoc: -------------------------------------------------------------------------------- 1 | Join us on https://gitter.im/asciidoctor/asciidoctor[Gitter to discuss Asciidoctor^]. -------------------------------------------------------------------------------- /test/scenarios/email/explicit.adoc: -------------------------------------------------------------------------------- 1 | If you want to know more about the mailto macro, send me an email at mailto:doc.writer@asciidoc.org[]! -------------------------------------------------------------------------------- /test/scenarios/keyboard/combo-trio.html: -------------------------------------------------------------------------------- 1 | New incognito window: Ctrl+Shift+N -------------------------------------------------------------------------------- /lib/asciidoctor/inline_parser/version.rb: -------------------------------------------------------------------------------- 1 | module Asciidoctor 2 | module InlineParser 3 | VERSION = '0.1.0'.freeze 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/scenarios/image/title.html: -------------------------------------------------------------------------------- 1 | Click pause when you need a break. -------------------------------------------------------------------------------- /test/scenarios/link/role.html: -------------------------------------------------------------------------------- 1 | Join us on Gitter to discuss Asciidoctor! -------------------------------------------------------------------------------- /test/scenarios/link/url-special-characters-encoded.html: -------------------------------------------------------------------------------- 1 | URL with special characters encoded -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /coverage/ 3 | /pkg/ 4 | /tmp/ 5 | Gemfile.lock 6 | *.bundle 7 | *.gem 8 | /.ruby-gemset 9 | /.ruby-version 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: false 3 | language: ruby 4 | rvm: 5 | - 2.5 6 | - 2.4 7 | - 2.3 8 | script: 9 | - bundle exec rake 10 | -------------------------------------------------------------------------------- /test/scenarios/email/tag.html: -------------------------------------------------------------------------------- 1 | You can support this project by becoming a sponsor: hello+donation@4fs.no -------------------------------------------------------------------------------- /test/scenarios/emphasis/unconstrained.adoc: -------------------------------------------------------------------------------- 1 | I want you to __re__read the article. You will find that she is _right_. Ba__boom__. ©__the authors__ -------------------------------------------------------------------------------- /test/scenarios/image/name-title.html: -------------------------------------------------------------------------------- 1 | Click Play to get the party started. -------------------------------------------------------------------------------- /test/scenarios/link/label.html: -------------------------------------------------------------------------------- 1 | Asciidoctor is a fast, open source text processor and publishing toolchain. -------------------------------------------------------------------------------- /test/scenarios/code/markup.adoc: -------------------------------------------------------------------------------- 1 | The HTML *Strong Importance Element (``)* indicates that its contents have strong importance, seriousness, or urgency. -------------------------------------------------------------------------------- /test/scenarios/email/body.html: -------------------------------------------------------------------------------- 1 | Doc Writer -------------------------------------------------------------------------------- /test/scenarios/image/positioning-role.html: -------------------------------------------------------------------------------- 1 | Sunset What a beautiful sunset! -------------------------------------------------------------------------------- /test/scenarios/link/window-blank-shorthand.html: -------------------------------------------------------------------------------- 1 | Join us on Gitter to discuss Asciidoctor. -------------------------------------------------------------------------------- /test/scenarios/literal/command-prompt.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
$ docker -it --run node:8-alpine
4 |
5 |
-------------------------------------------------------------------------------- /test/scenarios/email/explicit.html: -------------------------------------------------------------------------------- 1 | If you want to know more about the mailto macro, send me an email at doc.writer@asciidoc.org! -------------------------------------------------------------------------------- /test/scenarios/emphasis/unconstrained.html: -------------------------------------------------------------------------------- 1 | I want you to reread the article. You will find that she is right. Baboom. ©the authors -------------------------------------------------------------------------------- /test/scenarios/link/irc.html: -------------------------------------------------------------------------------- 1 | IRC (Internet Relay Chat) is a chat service used in Fedora community: irc://irc.freenode.org/#fedora -------------------------------------------------------------------------------- /test/scenarios/menu/submenu.html: -------------------------------------------------------------------------------- 1 | To save the file, select File  Save. -------------------------------------------------------------------------------- /test/scenarios/code/markup.html: -------------------------------------------------------------------------------- 1 | The HTML Strong Importance Element (<strong>) indicates that its contents have strong importance, seriousness, or urgency. -------------------------------------------------------------------------------- /test/scenarios/strong/unconstrained.adoc: -------------------------------------------------------------------------------- 1 | **C**reate. **R**ead. **U**pdate. **D**elete. Also known as *CRUD*. *CRUD*--the essential database operations. Not to be confused with E**CRU**, which is a color. ®**CRUD** it is not. -------------------------------------------------------------------------------- /test/scenarios/email/second-level-domain.adoc: -------------------------------------------------------------------------------- 1 | In a number of countries, .co is used as a second-level domain to mean "commercial" and domain registrants register second-level domains, for instance john@domain.co.uk in the United Kingdom. -------------------------------------------------------------------------------- /lib/asciidoctor/inline_parser/asciidoctor_btn_grammar.treetop: -------------------------------------------------------------------------------- 1 | grammar AsciidoctorBtnGrammar 2 | 3 | rule btn 4 | 'btn:[' btn_content ']' 5 | end 6 | 7 | rule btn_content 8 | ( '\]' / [^\]] )+ 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/asciidoctor/inline_parser/asciidoctor_kbd_grammar.treetop: -------------------------------------------------------------------------------- 1 | grammar AsciidoctorKbdGrammar 2 | 3 | rule kbd 4 | 'kbd:[' kbd_content ']' 5 | end 6 | 7 | rule kbd_content 8 | ( '\]' / [^\]] )+ 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/scenarios/email/second-level-domain.html: -------------------------------------------------------------------------------- 1 | In a number of countries, .co is used as a second-level domain to mean "commercial" and domain registrants register second-level domains, for instance john@domain.co.uk in the United Kingdom. -------------------------------------------------------------------------------- /test/scenarios/menu/subitem.html: -------------------------------------------------------------------------------- 1 | Select View  Zoom  Reset to reset the zoom level to the default setting. -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | 13 | [test/scenarios/**/*] 14 | insert_final_newline = false 15 | -------------------------------------------------------------------------------- /test/scenarios/strong/unconstrained.html: -------------------------------------------------------------------------------- 1 | Create. Read. Update. Delete. Also known as CRUD. CRUD--the essential database operations. Not to be confused with ECRU, which is a color. ®CRUD it is not. -------------------------------------------------------------------------------- /lib/asciidoctor/inline_parser/asciidoctor_menu_grammar.treetop: -------------------------------------------------------------------------------- 1 | grammar AsciidoctorMenuGrammar 2 | 3 | rule menu 4 | 'menu:' menu_target '[' menu_content ']' 5 | end 6 | 7 | rule menu_target 8 | [^\[]+ 9 | end 10 | 11 | rule menu_content 12 | ( '\]' / [^\]] )* 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | TargetRubyVersion: 2.3.0 3 | Exclude: 4 | - 'lib/asciidoctor/inline_parser/asciidoctor*grammar.rb' 5 | 6 | Metrics/LineLength: 7 | Max: 120 8 | Exclude: 9 | - 'test/*' 10 | 11 | Style/FrozenStringLiteralComment: 12 | Enabled: false 13 | 14 | Style/MethodDefParentheses: 15 | EnforcedStyle: require_no_parentheses 16 | 17 | Style/SafeNavigation: 18 | Enabled: false 19 | 20 | Style/SymbolArray: 21 | Enabled: false 22 | 23 | Metrics/BlockLength: 24 | Exclude: 25 | - 'test/*' 26 | 27 | Metrics/ModuleLength: 28 | Max: 200 29 | 30 | Metrics/MethodLength: 31 | Max: 20 32 | -------------------------------------------------------------------------------- /TODO.adoc: -------------------------------------------------------------------------------- 1 | = TODO 2 | 3 | * [x] Quotes 4 | ** [x] Constrained (strong, emphasis, mark, monospaced, subscript, superscript) 5 | ** [x] Unconstrained (strong, emphasis, mark, monospaced) 6 | * [x] Role assigned to quoted text (shorthand syntax `+[.rolename]#mark#+`) 7 | * [x] Smart quotes 8 | ** [x] Double curved quotes 9 | ** [x] Single curved quotes 10 | * [ ] Inline macros 11 | ** [x] `btn` 12 | ** [x] `menu` 13 | ** [x] `kbn` 14 | ** [x] `image` 15 | ** [x] `link` 16 | ** [x] `mailto` 17 | ** [ ] Custom 18 | * [ ] Passthrough 19 | * [ ] Replacements: should we reuse the we should https://github.com/asciidoctor/asciidoctor/blob/master/lib/asciidoctor/substitutors.rb[Substitutors.rb] ? 20 | * [x] Literal line 21 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require 'simplecov' 2 | SimpleCov.start 3 | 4 | require 'minitest/autorun' 5 | 6 | BACKSLASH = %(\x5c).freeze 7 | 8 | private def debug ast 9 | if ast 10 | p ast 11 | p ast.parent 12 | p ast.input 13 | p ast.text_value 14 | p ast.terminal? 15 | p ast.interval 16 | else 17 | p 'No match' 18 | end 19 | end 20 | 21 | private def find_by condition, node, result = [] 22 | result if node.nil? 23 | node.elements.each do |e| 24 | result << e if condition.call(e) 25 | find_by condition, e, result if e.elements && !e.elements.empty? 26 | end 27 | result 28 | end 29 | 30 | private def node_type_must_be name 31 | ->(node) { node.extension_modules.map(&:to_s).include? "AsciidoctorGrammar::#{name}0" } 32 | end 33 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = Asciidoctor Inline Parser 2 | ifdef::env-github[] 3 | :status: 4 | :outfilesuffix: .adoc 5 | :caution-caption: :fire: 6 | :important-caption: :exclamation: 7 | :note-caption: :paperclip: 8 | :tip-caption: :bulb: 9 | :warning-caption: :warning: 10 | endif::[] 11 | 12 | [caption=Status] 13 | CAUTION: This project is a work in progress. 14 | 15 | ifdef::status[] 16 | .*Project health* 17 | image:https://travis-ci.org/Mogztter/asciidoctor-inline-parser.svg?branch=master[Build Status (Travis CI), link=https://travis-ci.org/Mogztter/asciidoctor-inline-parser] 18 | endif::[] 19 | 20 | == Install 21 | 22 | Run `bundle` to install development dependencies. 23 | 24 | NOTE: If the `bundle` command is not available, run `gem install bundler` to install it. 25 | 26 | Run `bundle exec rake` to run the linter and the tests. 27 | -------------------------------------------------------------------------------- /asciidoctor-inline-parser.gemspec: -------------------------------------------------------------------------------- 1 | require_relative 'lib/asciidoctor/inline_parser/version' 2 | 3 | Gem::Specification.new do |s| 4 | s.name = 'asciidoctor-inline-parser' 5 | s.version = Asciidoctor::InlineParser::VERSION 6 | s.author = 'Guillaume Grossetie' 7 | s.email = 'ggrossetie@yuzutech.fr' 8 | s.homepage = 'https://github.com/mogztter/asciidoctor-inline-parser' 9 | s.license = 'MIT' 10 | 11 | s.summary = 'An inline parser for Asciidoctor' 12 | 13 | s.files = Dir['lib/**/*', '*.gemspec', 'LICENSE*', 'README*'] 14 | s.executables = Dir['bin/*'].map { |f| File.basename(f) } 15 | 16 | s.required_ruby_version = '>= 2.3.0' 17 | 18 | s.add_runtime_dependency 'asciidoctor', '~> 1.5.8' 19 | s.add_runtime_dependency 'treetop', '~> 1.6.10' 20 | 21 | s.add_development_dependency 'minitest', '~> 5.3.0' 22 | s.add_development_dependency 'rake', '~> 12.0' 23 | s.add_development_dependency 'rubocop', '~> 0.58.2' 24 | s.add_development_dependency 'simplecov', '~> 0.14' 25 | end 26 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | require 'rake/testtask' 3 | require 'rubocop/rake_task' 4 | require 'treetop' 5 | require 'polyglot' 6 | 7 | Rake::TestTask.new do |t| 8 | t.test_files = FileList['test/**/*_test.rb'] 9 | end 10 | desc 'Run tests' 11 | 12 | RuboCop::RakeTask.new(:rubocop) do |task| 13 | task.options << '--fail-fast' 14 | end 15 | desc 'Run RuboCop' 16 | 17 | task :compile do 18 | compiler = Treetop::Compiler::GrammarCompiler.new 19 | path = 'lib/asciidoctor/inline_parser/' 20 | treetop_grammar_files = File.join(path, '**', '*.treetop') 21 | Dir.glob(treetop_grammar_files).each do |treetop_grammar_file| 22 | target_file = "#{File.basename(treetop_grammar_file, '.treetop')}.rb" 23 | target = File.join(File.dirname(treetop_grammar_file), target_file) 24 | compiler.compile(treetop_grammar_file, target) 25 | end 26 | compiler.compile("#{path}/asciidoctor_grammar.treetop", "#{path}/asciidoctor_grammar.rb") 27 | end 28 | desc 'Compile .treetop files to Ruby' 29 | task default: [:compile, :rubocop, :test] 30 | -------------------------------------------------------------------------------- /test/scenarios_test.rb: -------------------------------------------------------------------------------- 1 | require_relative 'test_helper' 2 | require 'asciidoctor' 3 | require 'asciidoctor/inline_parser/parser' 4 | require_relative 'html5_converter' 5 | 6 | describe 'scenario' do 7 | let(:doc) { ::Asciidoctor::InlineParser.parse input } 8 | let(:converter) { ::Html5Converter.new } 9 | 10 | Dir.chdir File.join __dir__, 'scenarios' do 11 | (Dir.glob '*/*.adoc').each do |input_filename| 12 | input_stem = input_filename.slice 0, input_filename.length - 5 13 | scenario_name = input_stem.gsub '/', '::' 14 | input_filename = File.absolute_path input_filename 15 | output_filename = File.absolute_path %(#{input_stem}.html) 16 | describe %(for #{scenario_name}) do 17 | let(:input) { IO.read input_filename, mode: 'r:UTF-8', newline: :universal } 18 | let(:expected) { (IO.read output_filename, mode: 'r:UTF-8', newline: :universal).chomp } 19 | it 'converts inline Asciidoctor syntax to HTML' do 20 | (converter.inline doc).must_equal expected 21 | end 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/asciidoctor/inline_parser/asciidoctor_passthrough_grammar.treetop: -------------------------------------------------------------------------------- 1 | grammar AsciidoctorPassthroughGrammar 2 | 3 | rule escaped_passthrough_inline_macro 4 | '\\' passthrough_inline_macro 5 | end 6 | 7 | rule passthrough_inline_macro 8 | 'pass:' passthrough_inline_macro_subs '[' passthrough_inline_macro_content ']' 9 | end 10 | 11 | rule passthrough_inline_macro_subs 12 | ( ',' / passthrough_inline_macro_sub )* 13 | end 14 | 15 | rule passthrough_inline_macro_sub 16 | ( 17 | 'normal' / 'none' / 'specialcharacters' / 'specialchars' / 'quotes' / 'attributes' / 'replacements' / 'macros' / 'post_replacements' / 'verbatim' / 18 | 'c' / 'q' / 'a' / 'r' / 'm' / 'p' 19 | )+ 20 | end 21 | 22 | rule passthrough_inline_macro_content 23 | ( '\]' / [^\]] )* 24 | end 25 | 26 | rule passthrough_triple_plus 27 | '+++' passthrough_triple_plus_content '+++' 28 | end 29 | 30 | rule passthrough_triple_plus_content 31 | [^+]+ 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/asciidoctor/inline_parser/asciidoctor_image_grammar.treetop: -------------------------------------------------------------------------------- 1 | grammar AsciidoctorImageGrammar 2 | 3 | rule image 4 | 'image:' image_target image_attrs 5 | end 6 | 7 | rule image_target 8 | [^\[]+ 9 | end 10 | 11 | rule image_attrs 12 | '[' image_attrs_content ']' 13 | end 14 | 15 | rule image_attrs_content 16 | ( 17 | image_attr_text_protected / 18 | ',' / ' ' / 19 | image_named_attr / 20 | image_attr_text_unprotected 21 | )* 22 | end 23 | 24 | rule image_attr_text_unprotected 25 | [^,\]]+ 26 | end 27 | 28 | rule image_attr_text_protected 29 | '"' image_text_protected '"' 30 | end 31 | 32 | rule image_text_protected 33 | [^"]+ 34 | end 35 | 36 | rule image_named_attr 37 | image_named_attr_key '=' image_named_attr_value 38 | end 39 | 40 | rule image_named_attr_key 41 | [^,=]+ 42 | end 43 | 44 | rule image_named_attr_value 45 | ( image_text_protected / image_attr_text_protected ) 46 | end 47 | 48 | rule image_attr_value_unprotected 49 | [^,\]]+ 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/asciidoctor/inline_parser/asciidoctor_email_grammar.treetop: -------------------------------------------------------------------------------- 1 | grammar AsciidoctorEmailGrammar 2 | 3 | rule escaped_implicit_email 4 | '\\' implicit_email 5 | end 6 | 7 | rule escaped_explicit_email 8 | '\\' explicit_email 9 | end 10 | 11 | rule explicit_email 12 | 'mailto:' implicit_email email_attrs # TODO: Should we use a greedier rule since we are using an explicit link ? 13 | end 14 | 15 | rule implicit_email 16 | email_name '@' email_domain 17 | end 18 | 19 | rule email_name 20 | ( word / [\.%+\-] )+ 21 | end 22 | 23 | rule email_domain 24 | word ( email_tld / word )+ 25 | end 26 | 27 | rule email_tld 28 | '.' alpha 1..4 29 | end 30 | 31 | rule email_attrs 32 | '[' email_attrs_content ']' 33 | end 34 | 35 | rule email_attrs_content 36 | ( 37 | email_attr_text_protected / 38 | ',' / ' ' / 39 | email_attr_text_unprotected 40 | )* 41 | end 42 | 43 | rule email_attr_text_unprotected 44 | [^,\]]+ 45 | end 46 | 47 | rule email_attr_text_protected 48 | '"' email_text_protected '"' 49 | end 50 | 51 | rule email_text_protected 52 | [^"]+ 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/asciidoctor/inline_parser/parser.rb: -------------------------------------------------------------------------------- 1 | require 'treetop/runtime' 2 | 3 | require_relative 'node_extensions' 4 | require_relative 'asciidoctor_grammar' 5 | 6 | module Asciidoctor 7 | # Asciidoctor Inline Parser 8 | module InlineParser 9 | @parser = ::AsciidoctorGrammarParser.new 10 | 11 | def self.parse text 12 | # HACK: Use the following rule to parse a literal line. 13 | # Is it possible to implement the following Treetop rule: "when the line is indented with one or more spaces" ? 14 | # Since Treetop consumes the input, a given rule doesn't know if a matching space is at the start of the line. 15 | if text.start_with? ' ' 16 | text = text.strip 17 | return ::AsciidoctorGrammar::LiteralLine.new text, 0..text.length 18 | end 19 | ast = @parser.parse text 20 | return ast if ast.nil? 21 | clean_tree ast 22 | recurse ast 23 | ast 24 | end 25 | 26 | def self.recurse node 27 | return if node.elements.nil? 28 | node.elements.each do |el| 29 | if quoted_node? el 30 | ast = parse el.content 31 | assign_node ast, el unless ast.nil? 32 | end 33 | recurse el 34 | end 35 | end 36 | 37 | def self.quoted_node? node 38 | node.class.ancestors.include? ::AsciidoctorGrammar::QuotedNode 39 | end 40 | 41 | def self.clean_tree node 42 | return if node.elements.nil? 43 | node.elements.delete_if { |el| el.class.name == 'Treetop::Runtime::SyntaxNode' } 44 | node.elements.each { |el| clean_tree el } 45 | end 46 | 47 | def self.assign_node parent, node 48 | node.elements.clear 49 | parent.parent = node 50 | node.elements << parent 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/asciidoctor/inline_parser/asciidoctor_link_grammar.treetop: -------------------------------------------------------------------------------- 1 | grammar AsciidoctorLinkGrammar 2 | 3 | rule explicit_link 4 | 'link:' uri_scheme uri_path_explicit link_attrs? 5 | end 6 | 7 | rule explicit_link_protected 8 | 'link:++' uri_scheme uri_path_protected '++' link_attrs? 9 | end 10 | 11 | rule uri_path_explicit 12 | [^\[]+ 13 | end 14 | 15 | rule uri_path_protected 16 | [^+]+ 17 | end 18 | 19 | rule implicit_link 20 | uri_scheme uri_path link_attrs? 21 | end 22 | 23 | rule uri_scheme 24 | ( 'https' / 'http' / 'file' / 'ftp' / 'irc' ) '://' 25 | end 26 | 27 | rule uri_path 28 | ('.' (!' ' .) / [^ \.\[])+ 29 | end 30 | 31 | rule link_attrs 32 | '[' link_attrs_content ']' 33 | end 34 | 35 | rule link_attrs_content 36 | ( 37 | link_attr_role_protected / link_attr_role_unprotected / 38 | link_attr_window_protected / link_attr_text_protected / 39 | ',' / ' ' / 40 | link_attr_text_unprotected / link_attr_window_unprotected 41 | )+ 42 | end 43 | 44 | rule link_attr_text_unprotected 45 | [^,\]]+ 46 | end 47 | 48 | rule link_attr_text_protected 49 | '"' link_text_protected '"' 50 | end 51 | 52 | rule link_text_protected 53 | [^"]+ 54 | end 55 | 56 | rule link_attr_role_unprotected 57 | 'role=' [^,\]]+ 58 | end 59 | 60 | rule link_attr_role_protected 61 | 'role="' [^"]+ '"' 62 | end 63 | 64 | rule link_attr_window_unprotected 65 | 'window=' [^,\]]+ 66 | end 67 | 68 | rule link_attr_window_protected 69 | 'window="' [^"]+ '"' 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /lib/asciidoctor/inline_parser/asciidoctor_btn_grammar.rb: -------------------------------------------------------------------------------- 1 | # Autogenerated from a Treetop grammar. Edits may be lost. 2 | 3 | 4 | module AsciidoctorBtnGrammar 5 | include Treetop::Runtime 6 | 7 | def root 8 | @root ||= :btn 9 | end 10 | 11 | module Btn0 12 | def btn_content 13 | elements[1] 14 | end 15 | 16 | end 17 | 18 | def _nt_btn 19 | start_index = index 20 | if node_cache[:btn].has_key?(index) 21 | cached = node_cache[:btn][index] 22 | if cached 23 | node_cache[:btn][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 24 | @index = cached.interval.end 25 | end 26 | return cached 27 | end 28 | 29 | i0, s0 = index, [] 30 | if (match_len = has_terminal?('btn:[', false, index)) 31 | r1 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 32 | @index += match_len 33 | else 34 | terminal_parse_failure('\'btn:[\'') 35 | r1 = nil 36 | end 37 | s0 << r1 38 | if r1 39 | r2 = _nt_btn_content 40 | s0 << r2 41 | if r2 42 | if (match_len = has_terminal?(']', false, index)) 43 | r3 = true 44 | @index += match_len 45 | else 46 | terminal_parse_failure('\']\'') 47 | r3 = nil 48 | end 49 | s0 << r3 50 | end 51 | end 52 | if s0.last 53 | r0 = instantiate_node(Btn,input, i0...index, s0) 54 | r0.extend(Btn0) 55 | else 56 | @index = i0 57 | r0 = nil 58 | end 59 | 60 | node_cache[:btn][start_index] = r0 61 | 62 | r0 63 | end 64 | 65 | def _nt_btn_content 66 | start_index = index 67 | if node_cache[:btn_content].has_key?(index) 68 | cached = node_cache[:btn_content][index] 69 | if cached 70 | node_cache[:btn_content][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 71 | @index = cached.interval.end 72 | end 73 | return cached 74 | end 75 | 76 | s0, i0 = [], index 77 | loop do 78 | i1 = index 79 | if (match_len = has_terminal?('\]', false, index)) 80 | r2 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 81 | @index += match_len 82 | else 83 | terminal_parse_failure('\'\\]\'') 84 | r2 = nil 85 | end 86 | if r2 87 | r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true 88 | r1 = r2 89 | else 90 | if has_terminal?(@regexps[gr = '\A[^\\]]'] ||= Regexp.new(gr), :regexp, index) 91 | r3 = true 92 | @index += 1 93 | else 94 | terminal_parse_failure('[^\\]]') 95 | r3 = nil 96 | end 97 | if r3 98 | r3 = SyntaxNode.new(input, (index-1)...index) if r3 == true 99 | r1 = r3 100 | else 101 | @index = i1 102 | r1 = nil 103 | end 104 | end 105 | if r1 106 | s0 << r1 107 | else 108 | break 109 | end 110 | end 111 | if s0.empty? 112 | @index = i0 113 | r0 = nil 114 | else 115 | r0 = instantiate_node(BtnContent,input, i0...index, s0) 116 | end 117 | 118 | node_cache[:btn_content][start_index] = r0 119 | 120 | r0 121 | end 122 | 123 | end 124 | 125 | class AsciidoctorBtnGrammarParser < Treetop::Runtime::CompiledParser 126 | include AsciidoctorBtnGrammar 127 | end 128 | 129 | -------------------------------------------------------------------------------- /lib/asciidoctor/inline_parser/asciidoctor_kbd_grammar.rb: -------------------------------------------------------------------------------- 1 | # Autogenerated from a Treetop grammar. Edits may be lost. 2 | 3 | 4 | module AsciidoctorKbdGrammar 5 | include Treetop::Runtime 6 | 7 | def root 8 | @root ||= :kbd 9 | end 10 | 11 | module Kbd0 12 | def kbd_content 13 | elements[1] 14 | end 15 | 16 | end 17 | 18 | def _nt_kbd 19 | start_index = index 20 | if node_cache[:kbd].has_key?(index) 21 | cached = node_cache[:kbd][index] 22 | if cached 23 | node_cache[:kbd][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 24 | @index = cached.interval.end 25 | end 26 | return cached 27 | end 28 | 29 | i0, s0 = index, [] 30 | if (match_len = has_terminal?('kbd:[', false, index)) 31 | r1 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 32 | @index += match_len 33 | else 34 | terminal_parse_failure('\'kbd:[\'') 35 | r1 = nil 36 | end 37 | s0 << r1 38 | if r1 39 | r2 = _nt_kbd_content 40 | s0 << r2 41 | if r2 42 | if (match_len = has_terminal?(']', false, index)) 43 | r3 = true 44 | @index += match_len 45 | else 46 | terminal_parse_failure('\']\'') 47 | r3 = nil 48 | end 49 | s0 << r3 50 | end 51 | end 52 | if s0.last 53 | r0 = instantiate_node(Kbd,input, i0...index, s0) 54 | r0.extend(Kbd0) 55 | else 56 | @index = i0 57 | r0 = nil 58 | end 59 | 60 | node_cache[:kbd][start_index] = r0 61 | 62 | r0 63 | end 64 | 65 | def _nt_kbd_content 66 | start_index = index 67 | if node_cache[:kbd_content].has_key?(index) 68 | cached = node_cache[:kbd_content][index] 69 | if cached 70 | node_cache[:kbd_content][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 71 | @index = cached.interval.end 72 | end 73 | return cached 74 | end 75 | 76 | s0, i0 = [], index 77 | loop do 78 | i1 = index 79 | if (match_len = has_terminal?('\]', false, index)) 80 | r2 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 81 | @index += match_len 82 | else 83 | terminal_parse_failure('\'\\]\'') 84 | r2 = nil 85 | end 86 | if r2 87 | r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true 88 | r1 = r2 89 | else 90 | if has_terminal?(@regexps[gr = '\A[^\\]]'] ||= Regexp.new(gr), :regexp, index) 91 | r3 = true 92 | @index += 1 93 | else 94 | terminal_parse_failure('[^\\]]') 95 | r3 = nil 96 | end 97 | if r3 98 | r3 = SyntaxNode.new(input, (index-1)...index) if r3 == true 99 | r1 = r3 100 | else 101 | @index = i1 102 | r1 = nil 103 | end 104 | end 105 | if r1 106 | s0 << r1 107 | else 108 | break 109 | end 110 | end 111 | if s0.empty? 112 | @index = i0 113 | r0 = nil 114 | else 115 | r0 = instantiate_node(KbdContent,input, i0...index, s0) 116 | end 117 | 118 | node_cache[:kbd_content][start_index] = r0 119 | 120 | r0 121 | end 122 | 123 | end 124 | 125 | class AsciidoctorKbdGrammarParser < Treetop::Runtime::CompiledParser 126 | include AsciidoctorKbdGrammar 127 | end 128 | 129 | -------------------------------------------------------------------------------- /lib/asciidoctor/inline_parser/asciidoctor_menu_grammar.rb: -------------------------------------------------------------------------------- 1 | # Autogenerated from a Treetop grammar. Edits may be lost. 2 | 3 | 4 | module AsciidoctorMenuGrammar 5 | include Treetop::Runtime 6 | 7 | def root 8 | @root ||= :menu 9 | end 10 | 11 | module Menu0 12 | def menu_target 13 | elements[1] 14 | end 15 | 16 | def menu_content 17 | elements[3] 18 | end 19 | 20 | end 21 | 22 | def _nt_menu 23 | start_index = index 24 | if node_cache[:menu].has_key?(index) 25 | cached = node_cache[:menu][index] 26 | if cached 27 | node_cache[:menu][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 28 | @index = cached.interval.end 29 | end 30 | return cached 31 | end 32 | 33 | i0, s0 = index, [] 34 | if (match_len = has_terminal?('menu:', false, index)) 35 | r1 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 36 | @index += match_len 37 | else 38 | terminal_parse_failure('\'menu:\'') 39 | r1 = nil 40 | end 41 | s0 << r1 42 | if r1 43 | r2 = _nt_menu_target 44 | s0 << r2 45 | if r2 46 | if (match_len = has_terminal?('[', false, index)) 47 | r3 = true 48 | @index += match_len 49 | else 50 | terminal_parse_failure('\'[\'') 51 | r3 = nil 52 | end 53 | s0 << r3 54 | if r3 55 | r4 = _nt_menu_content 56 | s0 << r4 57 | if r4 58 | if (match_len = has_terminal?(']', false, index)) 59 | r5 = true 60 | @index += match_len 61 | else 62 | terminal_parse_failure('\']\'') 63 | r5 = nil 64 | end 65 | s0 << r5 66 | end 67 | end 68 | end 69 | end 70 | if s0.last 71 | r0 = instantiate_node(Menu,input, i0...index, s0) 72 | r0.extend(Menu0) 73 | else 74 | @index = i0 75 | r0 = nil 76 | end 77 | 78 | node_cache[:menu][start_index] = r0 79 | 80 | r0 81 | end 82 | 83 | def _nt_menu_target 84 | start_index = index 85 | if node_cache[:menu_target].has_key?(index) 86 | cached = node_cache[:menu_target][index] 87 | if cached 88 | node_cache[:menu_target][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 89 | @index = cached.interval.end 90 | end 91 | return cached 92 | end 93 | 94 | s0, i0 = [], index 95 | loop do 96 | if has_terminal?(@regexps[gr = '\A[^\\[]'] ||= Regexp.new(gr), :regexp, index) 97 | r1 = true 98 | @index += 1 99 | else 100 | terminal_parse_failure('[^\\[]') 101 | r1 = nil 102 | end 103 | if r1 104 | s0 << r1 105 | else 106 | break 107 | end 108 | end 109 | if s0.empty? 110 | @index = i0 111 | r0 = nil 112 | else 113 | r0 = instantiate_node(MenuTarget,input, i0...index, s0) 114 | end 115 | 116 | node_cache[:menu_target][start_index] = r0 117 | 118 | r0 119 | end 120 | 121 | def _nt_menu_content 122 | start_index = index 123 | if node_cache[:menu_content].has_key?(index) 124 | cached = node_cache[:menu_content][index] 125 | if cached 126 | node_cache[:menu_content][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 127 | @index = cached.interval.end 128 | end 129 | return cached 130 | end 131 | 132 | s0, i0 = [], index 133 | loop do 134 | i1 = index 135 | if (match_len = has_terminal?('\]', false, index)) 136 | r2 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 137 | @index += match_len 138 | else 139 | terminal_parse_failure('\'\\]\'') 140 | r2 = nil 141 | end 142 | if r2 143 | r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true 144 | r1 = r2 145 | else 146 | if has_terminal?(@regexps[gr = '\A[^\\]]'] ||= Regexp.new(gr), :regexp, index) 147 | r3 = true 148 | @index += 1 149 | else 150 | terminal_parse_failure('[^\\]]') 151 | r3 = nil 152 | end 153 | if r3 154 | r3 = SyntaxNode.new(input, (index-1)...index) if r3 == true 155 | r1 = r3 156 | else 157 | @index = i1 158 | r1 = nil 159 | end 160 | end 161 | if r1 162 | s0 << r1 163 | else 164 | break 165 | end 166 | end 167 | r0 = instantiate_node(MenuContent,input, i0...index, s0) 168 | 169 | node_cache[:menu_content][start_index] = r0 170 | 171 | r0 172 | end 173 | 174 | end 175 | 176 | class AsciidoctorMenuGrammarParser < Treetop::Runtime::CompiledParser 177 | include AsciidoctorMenuGrammar 178 | end 179 | 180 | -------------------------------------------------------------------------------- /test/html5_converter.rb: -------------------------------------------------------------------------------- 1 | # rubocop:disable Metrics/ClassLength 2 | class Html5Converter 3 | attr_reader :document 4 | 5 | def initialize 6 | @document = ::Asciidoctor::Document.new # FIXME: use the current Asciidoctor Document 7 | end 8 | 9 | def text node 10 | convert node.elements.first 11 | end 12 | 13 | def strong node 14 | "#{convert node.elements.first}" 15 | end 16 | 17 | def emphasis node 18 | "#{convert node.elements.first}" 19 | end 20 | 21 | def monospaced node 22 | "#{convert node.elements.first}" 23 | end 24 | 25 | def inline node 26 | text = node.text_value 27 | elements = node.terminal? ? node.elements : node.instance_variable_get('@comprehensive_elements') 28 | if elements.nil? 29 | convert node # workaround for LiteralLine 30 | elsif elements.empty? 31 | apply_sub text 32 | else 33 | elements.reverse_each do |el| 34 | text[el.interval] = convert el unless el.instance_of? ::Treetop::Runtime::SyntaxNode 35 | end 36 | text 37 | end 38 | end 39 | 40 | def double_quotation node 41 | "“#{convert node.elements.first}”" 42 | end 43 | 44 | def single_quotation node 45 | "‘#{convert node.elements.first}’" 46 | end 47 | 48 | def literal node 49 | node.elements.first.text_value 50 | end 51 | 52 | def literal_line node 53 | %(
54 |
55 |
#{node.text}
56 |
57 |
) 58 | end 59 | 60 | def anchor node 61 | roles_html = !node.roles.empty? ? %( class="#{node.roles.join(',')}") : '' 62 | %(#{node.text}) 63 | end 64 | 65 | def link_scheme node 66 | node.text_value 67 | end 68 | 69 | def link_path node 70 | node.text_value 71 | end 72 | 73 | def email_macro node 74 | subject_text = %(?subject=#{uri_encode_spaces node.subject}) if node.subject 75 | body_text = %(&body=#{uri_encode_spaces node.body}) if node.body 76 | link = %(#{node.email}#{subject_text}#{body_text}) 77 | %(#{node.name}) 78 | end 79 | 80 | def email node 81 | %(#{node.text}) 82 | end 83 | 84 | def escaped_email node 85 | text = node.text_value 86 | text[0] = '' 87 | text 88 | end 89 | 90 | def passthrough node 91 | node.text_value.gsub '\pass:', 'pass:' 92 | end 93 | 94 | def passthrough_inline node 95 | text = node.content 96 | node.apply_subs text 97 | end 98 | 99 | def passthrough_triple_plus node 100 | node.content 101 | end 102 | 103 | def image node 104 | width_html = node.width ? %( width="#{node.width}") : '' 105 | height_html = node.height ? %( height="#{node.height}") : '' 106 | class_html = node.roles.empty? ? '' : %(class="#{node.roles.join(' ')}") 107 | alt_html = node.alt ? %( alt="#{node.alt}") : '' 108 | title_html = node.title ? %( title="#{node.title}") : '' 109 | %() 110 | end 111 | 112 | def kbd node 113 | keys_html = lambda { |el| 114 | el.keys.map { |key| "#{(key.empty? ? '+' : key).strip.gsub '\]', ']'}" }.join '+' 115 | } 116 | if node.keys.size > 1 117 | %(#{keys_html.call node}) 118 | else 119 | keys_html.call node 120 | end 121 | end 122 | 123 | def btn node 124 | %(#{node.text}) 125 | end 126 | 127 | # rubocop:disable Metrics/AbcSize 128 | def menu node 129 | item_class = lambda { |index, size| 130 | if index.zero? 131 | 'menu' 132 | elsif index == size - 1 133 | 'menuitem' 134 | else 135 | 'submenu' 136 | end 137 | } 138 | items = node.items 139 | items_html = items.each_with_index.map do |item, index| 140 | item_text = item.strip.gsub '\]', ']' 141 | %(#{item_text}) 142 | end.join '  ' 143 | if items && items.size > 1 144 | %(#{items_html}) 145 | else 146 | %(#{items[0].strip.gsub '\]', ']'}) 147 | end 148 | end 149 | # rubocop:enable Metrics/AbcSize 150 | 151 | def convert node 152 | transform = node.node_name 153 | (send transform, node) 154 | end 155 | 156 | private 157 | 158 | def apply_sub text 159 | # TODO: Implement all the substitutions 160 | text = ::Asciidoctor::Subs.sub_specialchars text 161 | text = ::Asciidoctor::Subs.sub_attributes text, @document 162 | ::Asciidoctor::Subs.sub_replacements text 163 | end 164 | 165 | SPACE = ' '.freeze 166 | 167 | def uri_encode_spaces str 168 | if str.include? SPACE 169 | str.gsub SPACE, '%20' 170 | else 171 | str 172 | end 173 | end 174 | end 175 | # rubocop:enable Metrics/ClassLength 176 | -------------------------------------------------------------------------------- /lib/asciidoctor/inline_parser/asciidoctor_grammar.treetop: -------------------------------------------------------------------------------- 1 | require 'asciidoctor/inline_parser/asciidoctor_link_grammar' 2 | require 'asciidoctor/inline_parser/asciidoctor_email_grammar' 3 | require 'asciidoctor/inline_parser/asciidoctor_passthrough_grammar' 4 | require 'asciidoctor/inline_parser/asciidoctor_image_grammar' 5 | require 'asciidoctor/inline_parser/asciidoctor_kbd_grammar' 6 | require 'asciidoctor/inline_parser/asciidoctor_btn_grammar' 7 | require 'asciidoctor/inline_parser/asciidoctor_menu_grammar' 8 | 9 | grammar AsciidoctorGrammar 10 | 11 | include AsciidoctorLinkGrammar 12 | include AsciidoctorEmailGrammar 13 | include AsciidoctorPassthroughGrammar 14 | include AsciidoctorImageGrammar 15 | include AsciidoctorKbdGrammar 16 | include AsciidoctorBtnGrammar 17 | include AsciidoctorMenuGrammar 18 | 19 | rule text 20 | (quoted)* 21 | end 22 | 23 | rule quoted 24 | ( 25 | escaped_passthrough_inline_macro / escaped_quoted_symbol / escaped_role_symbol / escaped_explicit_email / escaped_implicit_email / 26 | image / 27 | kbd / btn / menu / 28 | passthrough_inline_macro / passthrough_triple_plus / 29 | double_curved_quotes / single_curved_quotes / 30 | literal / 31 | explicit_link / explicit_link_protected / implicit_link / 32 | explicit_email / implicit_email / 33 | unconstrained_strong / unconstrained_emphasis / unconstrained_monospaced / unconstrained_mark / 34 | strong / emphasis / monospaced / mark / superscript / subscript / 35 | ' ' / word / symbol 36 | )+ 37 | end 38 | 39 | rule double_curved_quotes 40 | '"`' double_curved_quotes_content '`"' 41 | end 42 | 43 | rule double_curved_quotes_content 44 | [^`"]+ 45 | end 46 | 47 | rule single_curved_quotes 48 | '\'`' single_curved_quotes_content '`\'' 49 | end 50 | 51 | rule single_curved_quotes_content 52 | [^`\']+ 53 | end 54 | 55 | rule literal 56 | '+' (!spaces) literal_content (!spaces) '+' 57 | end 58 | 59 | rule literal_content 60 | [^+]+ 61 | end 62 | 63 | rule strong 64 | quoted_text_attrs? '*' (!spaces) strong_content (!spaces) '*' (!constrained_mark_exception_end) 65 | end 66 | 67 | rule unconstrained_strong 68 | quoted_text_attrs? '**' strong_content '**' 69 | end 70 | 71 | rule strong_content 72 | (strong_content_greedy)+ 73 | end 74 | 75 | rule strong_content_greedy 76 | ( [^*] / '*' constrained_mark_exception ) 77 | end 78 | 79 | rule emphasis 80 | quoted_text_attrs? '_' (!spaces) emphasis_content (!spaces) '_' (!constrained_mark_exception_end) 81 | end 82 | 83 | rule unconstrained_emphasis 84 | quoted_text_attrs? '__' emphasis_content '__' 85 | end 86 | 87 | rule emphasis_content 88 | (emphasis_content_greedy)+ 89 | end 90 | 91 | rule emphasis_content_greedy 92 | ( [^_] / '_' constrained_mark_exception ) 93 | end 94 | 95 | rule monospaced 96 | quoted_text_attrs? '`' (!spaces) monospaced_content (!spaces) '`' (!constrained_mark_exception_end) 97 | end 98 | 99 | rule unconstrained_monospaced 100 | quoted_text_attrs? '``' monospaced_content '``' 101 | end 102 | 103 | rule monospaced_content 104 | (monospaced_content_greedy)+ 105 | end 106 | 107 | rule monospaced_content_greedy 108 | ( [^`] / '`' constrained_mark_exception ) 109 | end 110 | 111 | rule mark 112 | quoted_text_attrs? '#' (!spaces) mark_content (!spaces) '#' (!constrained_mark_exception_end) 113 | end 114 | 115 | rule unconstrained_mark 116 | quoted_text_attrs? '##' mark_content '##' 117 | end 118 | 119 | rule mark_content 120 | (mark_content_greedy)+ 121 | end 122 | 123 | rule mark_content_greedy 124 | ( [^#] / '#' constrained_mark_exception ) 125 | end 126 | 127 | rule superscript 128 | '^' (!spaces) superscript_content (!spaces) '^' 129 | end 130 | 131 | rule superscript_content 132 | [^\^]+ 133 | end 134 | 135 | rule subscript 136 | '~' (!spaces) subscript_content (!spaces) '~' 137 | end 138 | 139 | rule subscript_content 140 | [^~]+ 141 | end 142 | 143 | rule quoted_text_attrs 144 | '[' quoted_text_attrs_content ']' 145 | end 146 | 147 | rule quoted_text_attrs_content 148 | ( quoted_text_anchor / quoted_text_role )* 149 | end 150 | 151 | rule quoted_text_role 152 | '.' role_identifier 153 | end 154 | 155 | rule quoted_text_anchor 156 | '#' anchor_identifier 157 | end 158 | 159 | rule constrained_mark_exception_end 160 | ( constrained_mark_exception ) 161 | end 162 | 163 | rule constrained_mark_exception 164 | '[\p{Word}&&[^_]]'r 165 | end 166 | 167 | rule escaped_quoted_symbol 168 | ( '\*' / '\_' / '\`' / '\#' / '\^' / '\~' ) 169 | end 170 | 171 | rule escaped_role_symbol 172 | ( '\[' ) 173 | end 174 | 175 | rule symbol 176 | ( symbol_basic / quoted_symbol ) 177 | end 178 | 179 | rule symbol_basic 180 | [&\-:;=,"'\.!\\{}\]\[<>/()] 181 | end 182 | 183 | rule quoted_symbol 184 | [_*#`^~]+ 185 | end 186 | 187 | rule role_identifier 188 | identifier+ 189 | end 190 | 191 | rule anchor_identifier 192 | identifier+ 193 | end 194 | 195 | rule identifier 196 | [0-9a-zA-Z_\-] 197 | end 198 | 199 | rule word 200 | alnum 201 | end 202 | 203 | rule number 204 | [0-9]+ 205 | end 206 | 207 | rule alpha 208 | [a-zA-Z]+ # FIXME: should include all unicode characters ? 209 | end 210 | 211 | rule alnum 212 | ( number / alpha ) 213 | end 214 | 215 | rule spaces 216 | [ ]+ 217 | end 218 | end 219 | -------------------------------------------------------------------------------- /lib/asciidoctor/inline_parser/subs.rb: -------------------------------------------------------------------------------- 1 | # Asciidoctor 2 | module Asciidoctor 3 | # Public: Methods to perform substitutions on lines of AsciiDoc text. 4 | module Subs 5 | SPECIAL_CHARS_RX = /[<&>]/ 6 | SPECIAL_CHARS_TR = { '>' => '>', '<' => '<', '&' => '&' }.freeze 7 | REPLACEABLE_TEXT_RX = /[&']|--|\.\.\.|\([CRT]M?\)/ 8 | 9 | if ::RUBY_MIN_VERSION_1_9 10 | CAN = %(\u0018).freeze 11 | DEL = %(\u007f).freeze 12 | 13 | # Delimiters and matchers for the passthrough placeholder 14 | # See http://www.aivosto.com/vbtips/control-characters.html#listabout for characters to use 15 | 16 | # SPA, start of guarded protected area (\u0096) 17 | PASS_START = %(\u0096).freeze 18 | 19 | # EPA, end of guarded protected area (\u0097) 20 | PASS_END = %(\u0097).freeze 21 | else 22 | CAN = 24.chr 23 | DEL = 127.chr 24 | PASS_START = 150.chr 25 | PASS_END = 151.chr 26 | end 27 | 28 | RS = '\\'.freeze 29 | 30 | class << self 31 | # Public: Substitute special characters (i.e., encode XML) 32 | # 33 | # The special characters <, &, and > get replaced with <, 34 | # &, and >, respectively. 35 | # 36 | # text - The String text to process. 37 | # 38 | # returns The String text with special characters replaced. 39 | if ::RUBY_MIN_VERSION_1_9 40 | def sub_specialchars text 41 | if (text.include? '<') || (text.include? '&') || (text.include? '>') 42 | (text.gsub SPECIAL_CHARS_RX, SPECIAL_CHARS_TR) 43 | else 44 | text 45 | end 46 | end 47 | else 48 | def sub_specialchars text 49 | if (text.include? '<') || (text.include? '&') || (text.include? '>') 50 | (text.gsub(SPECIAL_CHARS_RX) { SPECIAL_CHARS_TR[$&] }) 51 | else 52 | text 53 | end 54 | end 55 | end 56 | alias sub_specialcharacters sub_specialchars 57 | 58 | # Public: Substitutes attribute references in the specified text 59 | # 60 | # Attribute references are in the format +{name}+. 61 | # 62 | # If an attribute referenced in the line is missing or undefined, the line may be dropped 63 | # based on the attribute-missing or attribute-undefined setting, respectively. 64 | # 65 | # text - The String text to process 66 | # doc - the Document being parsed 67 | # opts - A Hash of options to control processing: (default: {}) 68 | # * :attribute_missing controls how to handle a missing attribute 69 | # 70 | # Returns the [String] text with the attribute references replaced with resolved values 71 | 72 | # rubocop:disable Metrics/AbcSize 73 | # rubocop:disable Metrics/CyclomaticComplexity 74 | # rubocop:disable Metrics/MethodLength 75 | # rubocop:disable Metrics/BlockLength 76 | # rubocop:disable Metrics/PerceivedComplexity 77 | def sub_attributes text, doc = nil, opts = {} 78 | doc_attrs = doc.attributes 79 | drop = drop_line = drop_empty_line = nil 80 | attribute_undefined ||= doc_attrs['attribute-undefined'] || 'drop-line' 81 | attribute_missing ||= opts[:attribute_missing] || doc_attrs['attribute-missing'] || 'skip' 82 | result = text.gsub AttributeReferenceRx do 83 | # escaped attribute, return unescaped 84 | if Regexp.last_match(1) == RS || Regexp.last_match(4) == RS 85 | %({#{Regexp.last_match(2)}}) 86 | elsif Regexp.last_match(3) 87 | case (args = Regexp.last_match(2).split ':', 3).shift 88 | when 'set' 89 | _, value = Parser.store_attribute args[0], args[1] || '', doc 90 | # NOTE since this is an assignment, only drop-line applies here (skip and drop imply the same result) 91 | drop = if value || attribute_undefined != 'drop-line' 92 | drop_empty_line = DEL 93 | else 94 | drop_line = CAN 95 | end 96 | when 'counter2' 97 | doc.counter(*args) 98 | drop = drop_empty_line = DEL 99 | else # 'counter' 100 | doc.counter(*args) 101 | end 102 | elsif doc_attrs.key?((key = Regexp.last_match(2).downcase)) 103 | doc_attrs[key] 104 | elsif (value = INTRINSIC_ATTRIBUTES[key]) 105 | value 106 | else 107 | case attribute_missing 108 | when 'drop' 109 | drop = drop_empty_line = DEL 110 | when 'drop-line' 111 | logger.warn %(dropping line containing reference to missing attribute: #{key}) 112 | drop = drop_line = CAN 113 | when 'warn' 114 | logger.warn %(skipping reference to missing attribute: #{key}) 115 | $& 116 | else # 'skip' 117 | $& 118 | end 119 | end 120 | end 121 | 122 | if drop 123 | # drop lines from result 124 | if drop_empty_line 125 | lines = (result.tr_s DEL, DEL).split LF, -1 126 | if drop_line 127 | (lines.reject do |line| 128 | line == DEL || line == CAN || (line.start_with? CAN) || (line.include? CAN) 129 | end.join LF).delete DEL 130 | else 131 | (lines.reject { |line| line == DEL }.join LF).delete DEL 132 | end 133 | elsif result.include? LF 134 | (result.split LF, -1).reject { |line| line == CAN || (line.start_with? CAN) || (line.include? CAN) }.join LF 135 | else 136 | '' 137 | end 138 | else 139 | result 140 | end 141 | end 142 | 143 | # rubocop:enable Metrics/AbcSize 144 | # rubocop:enable Metrics/CyclomaticComplexity 145 | # rubocop:enable Metrics/MethodLength 146 | # rubocop:enable Metrics/BlockLength 147 | # rubocop:enable Metrics/PerceivedComplexity 148 | 149 | if RUBY_ENGINE == 'opal' 150 | def sub_replacements text 151 | if REPLACEABLE_TEXT_RX.match? text 152 | REPLACEMENTS.each do |pattern, replacement, restore| 153 | text = text.gsub(pattern) { do_replacement $LAST_MATCH_INFO, replacement, restore } 154 | end 155 | end 156 | text 157 | end 158 | else 159 | # Public: Substitute replacement characters (e.g., copyright, trademark, etc) 160 | # 161 | # text - The String text to process 162 | # 163 | # returns The String text with the replacement characters substituted 164 | def sub_replacements text 165 | if REPLACEABLE_TEXT_RX.match? text 166 | # NOTE interpolation is faster than String#dup 167 | text = text.to_s 168 | REPLACEMENTS.each do |pattern, replacement, restore| 169 | # NOTE Using gsub! as optimization 170 | text.gsub!(pattern) { do_replacement $LAST_MATCH_INFO, replacement, restore } 171 | end 172 | end 173 | text 174 | end 175 | end 176 | 177 | # Internal: Substitute replacement text for matched location 178 | # 179 | # returns The String text with the replacement characters substituted 180 | def do_replacement match, replacement, restore 181 | if (captured = match[0]).include? RS 182 | # we have to use sub since we aren't sure it's the first char 183 | captured.sub RS, '' 184 | else 185 | case restore 186 | when :none 187 | replacement 188 | when :bounding 189 | %(#{match[1]}#{replacement}#{m[2]}) 190 | else # :leading 191 | %(#{match[1]}#{replacement}) 192 | end 193 | end 194 | end 195 | end 196 | end 197 | end 198 | -------------------------------------------------------------------------------- /test/quoted_text_test.rb: -------------------------------------------------------------------------------- 1 | require_relative 'test_helper' 2 | require 'asciidoctor' 3 | require 'asciidoctor/inline_parser/parser' 4 | 5 | describe 'quoted text' do 6 | quotes = [ 7 | # *strong* (constrained) or **strong** (unconstrained) 8 | { name: :strong, symbol: '*', type: 'Strong' }, 9 | 10 | # `monospaced` (constrained) or ``monospaced`` (unconstrained) 11 | { name: :monospaced, symbol: '`', type: 'Monospaced' }, 12 | 13 | # _emphasis_ (constrained) or __emphasis__ (unconstrained) 14 | { name: :emphasis, symbol: '_', type: 'Emphasis' }, 15 | 16 | # #mark# (constrained) ##mark## (unconstrained) 17 | { name: :mark, symbol: '#', type: 'Mark' } 18 | ] 19 | constrained_quotes = quotes + [ 20 | # ^superscript^ (constrained) 21 | { name: :superscript, symbol: '^', type: 'Superscript' }, 22 | # ~subscript~ (constrained) 23 | { name: :subscript, symbol: '~', type: 'Subscript' } 24 | ] 25 | 26 | constrained_quotes.each do |constrained_quote| 27 | name = constrained_quote[:name] 28 | symbol = constrained_quote[:symbol] 29 | type = constrained_quote[:type] 30 | describe "constrained #{name}" do 31 | it 'should parse a single-line constrained word' do 32 | input = %(#{symbol}word#{symbol}) 33 | ast = ::Asciidoctor::InlineParser.parse input 34 | ast.text_value.must_equal input 35 | nodes = find_by (node_type_must_be type), ast 36 | nodes.size.must_equal 1 37 | nodes.first.text_value.must_equal input 38 | nodes.first.content.must_equal 'word' 39 | end 40 | it 'should parse a single-line constrained string' do 41 | input = %(#{symbol}a few words#{symbol}) 42 | ast = ::Asciidoctor::InlineParser.parse input 43 | ast.text_value.must_equal input 44 | nodes = find_by (node_type_must_be type), ast 45 | nodes.size.must_equal 1 46 | nodes.first.text_value.must_equal input 47 | nodes.first.content.must_equal 'a few words' 48 | end 49 | it 'should parse a single-line constrained string in a sentence' do 50 | input = "I want to say #{symbol}a few words#{symbol}." 51 | ast = ::Asciidoctor::InlineParser.parse input 52 | ast.text_value.must_equal input 53 | nodes = find_by (node_type_must_be type), ast 54 | nodes.size.must_equal 1 55 | nodes.first.text_value.must_equal "#{symbol}a few words#{symbol}" 56 | nodes.first.content.must_equal 'a few words' 57 | end 58 | it 'should parse an escaped single-line constrained string' do 59 | input = "#{BACKSLASH}#{symbol}a few words#{symbol}" 60 | ast = ::Asciidoctor::InlineParser.parse input 61 | ast.text_value.must_equal input 62 | nodes = find_by (node_type_must_be type), ast 63 | nodes.size.must_equal 0 64 | end 65 | it 'should parse a multi-line constrained string' do 66 | input = "#{symbol}a few\nwords#{symbol}" 67 | ast = ::Asciidoctor::InlineParser.parse input 68 | ast.text_value.must_equal input 69 | nodes = find_by (node_type_must_be type), ast 70 | nodes.size.must_equal 1 71 | nodes.first.text_value.must_equal input 72 | nodes.first.content.must_equal %(a few\nwords) 73 | end 74 | end 75 | end 76 | 77 | quotes.each do |unconstrained_quote| 78 | name = unconstrained_quote[:name] 79 | symbol = unconstrained_quote[:symbol] 80 | type = unconstrained_quote[:type] 81 | describe "unconstrained #{name}" do 82 | it 'should parse a single-line constrained word' do 83 | input = %(#{symbol}word#{symbol}) 84 | ast = ::Asciidoctor::InlineParser.parse input 85 | ast.text_value.must_equal input 86 | nodes = find_by (node_type_must_be type), ast 87 | nodes.size.must_equal 1 88 | nodes.first.text_value.must_equal input 89 | nodes.first.content.must_equal 'word' 90 | end 91 | it 'should parse a single-line constrained string' do 92 | input = %(#{symbol}a few words#{symbol}) 93 | ast = ::Asciidoctor::InlineParser.parse input 94 | ast.text_value.must_equal input 95 | nodes = find_by (node_type_must_be type), ast 96 | nodes.size.must_equal 1 97 | nodes.first.text_value.must_equal input 98 | nodes.first.content.must_equal 'a few words' 99 | end 100 | it 'should parse a single-line constrained string in a sentence' do 101 | input = "I want to say #{symbol}a few words#{symbol}." 102 | ast = ::Asciidoctor::InlineParser.parse input 103 | ast.text_value.must_equal input 104 | nodes = find_by (node_type_must_be type), ast 105 | nodes.size.must_equal 1 106 | nodes.first.text_value.must_equal "#{symbol}a few words#{symbol}" 107 | nodes.first.content.must_equal 'a few words' 108 | end 109 | it 'should parse an escaped single-line constrained string' do 110 | input = "#{BACKSLASH}#{symbol}a few words#{symbol}" 111 | ast = ::Asciidoctor::InlineParser.parse input 112 | ast.text_value.must_equal input 113 | nodes = find_by (node_type_must_be type), ast 114 | nodes.size.must_equal 0 115 | end 116 | it 'should parse a multi-line constrained string' do 117 | input = "#{symbol}a few\nwords#{symbol}" 118 | ast = ::Asciidoctor::InlineParser.parse input 119 | ast.text_value.must_equal input 120 | nodes = find_by (node_type_must_be type), ast 121 | nodes.size.must_equal 1 122 | nodes.first.text_value.must_equal input 123 | nodes.first.content.must_equal %(a few\nwords) 124 | end 125 | it 'should not parse a constrained string with a number directly outside the formatting marks' do 126 | input = "E = #{symbol}mc#{symbol}2" 127 | ast = ::Asciidoctor::InlineParser.parse input 128 | ast.text_value.must_equal input 129 | nodes = find_by (node_type_must_be type), ast 130 | nodes.size.must_equal 0 131 | end 132 | it 'should parse a constrained string with a colon directly before the starting formatting mark' do 133 | input = "There's a colon:#{symbol}directly#{symbol} before the starting formatting mark." 134 | ast = ::Asciidoctor::InlineParser.parse input 135 | ast.text_value.must_equal input 136 | nodes = find_by (node_type_must_be type), ast 137 | nodes.size.must_equal 1 138 | nodes.first.text_value.must_equal "#{symbol}directly#{symbol}" 139 | nodes.first.content.must_equal 'directly' 140 | end 141 | it 'should parse a constrained string with a semi-colon directly before the starting formatting mark' do 142 | input = "There's a semi-colon directly before the starting formatting mark –#{symbol}2018#{symbol}" 143 | ast = ::Asciidoctor::InlineParser.parse input 144 | ast.text_value.must_equal input 145 | nodes = find_by (node_type_must_be type), ast 146 | nodes.first.text_value.must_equal "#{symbol}2018#{symbol}" 147 | nodes.first.content.must_equal '2018' 148 | end 149 | it 'should parse a constrained string with a closing curly bracket directly before the starting formatting mark' do 150 | input = "There's a closing curly bracket directly {before}#{symbol}the starting formatting mark#{symbol}." 151 | ast = ::Asciidoctor::InlineParser.parse input 152 | ast.text_value.must_equal input 153 | nodes = find_by (node_type_must_be type), ast 154 | nodes.first.text_value.must_equal "#{symbol}the starting formatting mark#{symbol}" 155 | nodes.first.content.must_equal 'the starting formatting mark' 156 | end 157 | ## Constrained quote limitations 158 | # there's a letter directly outside the formatting marks 159 | it 'should parse a constrained string with a letter directly outside the formatting marks' do 160 | input = "There's a l#{symbol}e#{symbol}tter directly outside the formatting marks." 161 | ast = ::Asciidoctor::InlineParser.parse input 162 | ast.text_value.must_equal input 163 | nodes = find_by (node_type_must_be type), ast 164 | nodes.size.must_equal 0 165 | end 166 | # there's a space directly inside of the formatting mark 167 | it 'should parse a constrained string with inner spaces' do 168 | input = "There's a #{symbol} space #{symbol} directly inside of the formatting mark" 169 | ast = ::Asciidoctor::InlineParser.parse input 170 | ast.text_value.must_equal input 171 | nodes = find_by (node_type_must_be type), ast 172 | nodes.size.must_equal 0 173 | end 174 | end 175 | end 176 | end 177 | -------------------------------------------------------------------------------- /test/inline_parser_test.rb: -------------------------------------------------------------------------------- 1 | require_relative 'test_helper' 2 | require 'asciidoctor' 3 | require 'asciidoctor/inline_parser/parser' 4 | 5 | describe 'inline parser' do 6 | it 'should parse a single-line unconstrained marked string' do 7 | ast = ::Asciidoctor::InlineParser.parse %(##--anything goes ##) 8 | ast.text_value.must_equal %(##--anything goes ##) 9 | nodes = find_by (node_type_must_be 'UnconstrainedMark'), ast 10 | nodes.size.must_equal 1 11 | nodes.first.text_value.must_equal %(##--anything goes ##) 12 | nodes.first.content.must_equal %(--anything goes ) 13 | end 14 | it 'should parse an escaped single-line unconstrained marked string' do 15 | ast = ::Asciidoctor::InlineParser.parse %(#{BACKSLASH}#{BACKSLASH}##--anything goes ##) 16 | ast.text_value.must_equal %(#{BACKSLASH}#{BACKSLASH}##--anything goes ##) 17 | nodes = find_by (node_type_must_be 'UnconstrainedMark'), ast 18 | nodes.size.must_equal 0 19 | end 20 | it 'should parse a single-line constrained marked string with role' do 21 | ast = ::Asciidoctor::InlineParser.parse '[.statement]#a few words#' 22 | ast.text_value.must_equal '[.statement]#a few words#' 23 | nodes = find_by (node_type_must_be 'Mark'), ast 24 | nodes.size.must_equal 1 25 | nodes.first.roles.must_equal ['statement'] 26 | end 27 | it 'should parse a constrained strong string containing an asterisk' do 28 | ast = ::Asciidoctor::InlineParser.parse '*bl*ck*-eye' 29 | ast.text_value.must_equal '*bl*ck*-eye' 30 | nodes = find_by (node_type_must_be 'Strong'), ast 31 | nodes.size.must_equal 1 32 | nodes.first.text_value.must_equal '*bl*ck*' 33 | nodes.first.content.must_equal 'bl*ck' 34 | end 35 | it 'should parse a constrained strong string containing an asterisk and multibyte word chars' do 36 | # FIXME: multibyte word chars are not yet supported 37 | skip 'multibyte word chars are not yet supported' 38 | ast = ::Asciidoctor::InlineParser.parse '*黑*眼圈*' 39 | ast.text_value.must_equal '*黑*眼圈*' 40 | nodes = find_by (node_type_must_be 'Strong'), ast 41 | nodes.size.must_equal 1 42 | nodes.first.text_value.must_equal '*黑*眼圈*' 43 | nodes.first.content.must_equal '黑*眼圈' 44 | end 45 | it 'should parse a single-line unconstrained strong chars' do 46 | ast = ::Asciidoctor::InlineParser.parse '**Git**Hub' 47 | ast.text_value.must_equal '**Git**Hub' 48 | nodes = find_by (node_type_must_be 'UnconstrainedStrong'), ast 49 | nodes.size.must_equal 1 50 | nodes.first.text_value.must_equal '**Git**' 51 | nodes.first.content.must_equal 'Git' 52 | end 53 | it 'should parse a multi-line unconstrained strong chars' do 54 | ast = ::Asciidoctor::InlineParser.parse "**G\ni\nt\n**Hub" 55 | ast.text_value.must_equal "**G\ni\nt\n**Hub" 56 | nodes = find_by (node_type_must_be 'UnconstrainedStrong'), ast 57 | nodes.size.must_equal 1 58 | nodes.first.text_value.must_equal "**G\ni\nt\n**" 59 | nodes.first.content.must_equal "G\ni\nt\n" 60 | end 61 | it 'should parse a unconstrained strong chars with inline asterisk' do 62 | ast = ::Asciidoctor::InlineParser.parse '**bl*ck**-eye' 63 | ast.text_value.must_equal '**bl*ck**-eye' 64 | nodes = find_by (node_type_must_be 'UnconstrainedStrong'), ast 65 | nodes.size.must_equal 1 66 | nodes.first.text_value.must_equal '**bl*ck**' 67 | nodes.first.content.must_equal 'bl*ck' 68 | end 69 | it 'should parse a unconstrained strong chars with role' do 70 | ast = ::Asciidoctor::InlineParser.parse 'Git[.blue]**Hub**' 71 | ast.text_value.must_equal 'Git[.blue]**Hub**' 72 | nodes = find_by (node_type_must_be 'UnconstrainedStrong'), ast 73 | nodes.size.must_equal 1 74 | nodes.first.roles.must_equal ['blue'] 75 | end 76 | # REMIND: this is not the same result as AsciiDoc, though I don't understand why AsciiDoc gets what it gets 77 | it 'should parse a escaped unconstrained strong chars with role' do 78 | ast = ::Asciidoctor::InlineParser.parse %(Git#{BACKSLASH}[.blue]**Hub**) 79 | ast.text_value.must_equal %(Git#{BACKSLASH}[.blue]**Hub**) 80 | nodes = find_by (node_type_must_be 'UnconstrainedStrong'), ast 81 | nodes.size.must_equal 1 82 | nodes.first.text_value.must_equal '**Hub**' 83 | nodes.first.content.must_equal 'Hub' 84 | end 85 | it 'should parse a single-line unconstrained emphasized chars' do 86 | ast = ::Asciidoctor::InlineParser.parse '__Git__Hub' 87 | ast.text_value.must_equal '__Git__Hub' 88 | nodes = find_by (node_type_must_be 'UnconstrainedEmphasis'), ast 89 | nodes.size.must_equal 1 90 | nodes.first.text_value.must_equal '__Git__' 91 | nodes.first.content.must_equal 'Git' 92 | end 93 | it 'should parse an escaped single-line unconstrained strong chars' do 94 | # FIXME: Unexpected result, let's discuss it! 95 | skip 'Unexpected result. I would expect \**Git**Hub to be equivalent to *Git**Hub (with a trailing *)' 96 | ast = ::Asciidoctor::InlineParser.parse %(#{BACKSLASH}**Git**Hub) 97 | ast.text_value.must_equal %(#{BACKSLASH}**Git**Hub) 98 | nodes = find_by (node_type_must_be 'UnconstrainedStrong'), ast 99 | nodes.size.must_equal 1 100 | nodes.first.text_value.must_equal '**Git*' 101 | nodes.first.content.must_equal '*Git' 102 | # expected result: *Git*Hub 103 | end 104 | it 'should parse a escaped single-line unconstrained emphasized chars' do 105 | # FIXME: Unexpected result, let's discuss it! 106 | skip 'Unexpected result. I would expect \__Git__Hub to be consistent with \**Git**Hub' 107 | ast = ::Asciidoctor::InlineParser.parse %(#{BACKSLASH}__Git__Hub) 108 | ast.text_value.must_equal %(#{BACKSLASH}__Git__Hub) 109 | nodes = find_by (node_type_must_be 'UnconstrainedEmphasis'), ast 110 | nodes.size.must_equal 0 111 | # QUESTION: Why the behavior is not consistent with the previous escaped unconstrained strong ? 112 | end 113 | it 'should parse a escaped single-line unconstrained emphasized chars around word' do 114 | # FIXME: Unexpected result, let's discuss it! 115 | skip 'Unexpected result. Im lost :|' 116 | ast = ::Asciidoctor::InlineParser.parse %(#{BACKSLASH}#{BACKSLASH}__GitHub__) 117 | ast.text_value.must_equal %(#{BACKSLASH}#{BACKSLASH}__GitHub__) 118 | nodes = find_by (node_type_must_be 'UnconstrainedEmphasis'), ast 119 | nodes.size.must_equal 0 120 | end 121 | it 'should parse a multi-line unconstrained emphasized chars' do 122 | ast = ::Asciidoctor::InlineParser.parse "__G\ni\nt\n__Hub" 123 | ast.text_value.must_equal "__G\ni\nt\n__Hub" 124 | nodes = find_by (node_type_must_be 'UnconstrainedEmphasis'), ast 125 | nodes.size.must_equal 1 126 | nodes.first.text_value.must_equal "__G\ni\nt\n__" 127 | nodes.first.content.must_equal "G\ni\nt\n" 128 | end 129 | it 'should parse a unconstrained emphasis chars with role' do 130 | ast = ::Asciidoctor::InlineParser.parse '[.gray]__Git__Hub' 131 | ast.text_value.must_equal '[.gray]__Git__Hub' 132 | nodes = find_by (node_type_must_be 'UnconstrainedEmphasis'), ast 133 | nodes.size.must_equal 1 134 | nodes.first.roles.must_equal ['gray'] 135 | nodes.first.content.must_equal 'Git' 136 | end 137 | it 'should parse a unconstrained emphasis chars with roles' do 138 | ast = ::Asciidoctor::InlineParser.parse '[.gray.small]__Git__Hub' 139 | ast.text_value.must_equal '[.gray.small]__Git__Hub' 140 | nodes = find_by (node_type_must_be 'UnconstrainedEmphasis'), ast 141 | nodes.size.must_equal 1 142 | nodes.first.roles.must_equal %w[gray small] 143 | nodes.first.content.must_equal 'Git' 144 | end 145 | it 'should parse a unconstrained emphasis chars with roles and id' do 146 | ast = ::Asciidoctor::InlineParser.parse '[#git.gray.small]__Git__Hub' 147 | ast.text_value.must_equal '[#git.gray.small]__Git__Hub' 148 | nodes = find_by (node_type_must_be 'UnconstrainedEmphasis'), ast 149 | nodes.size.must_equal 1 150 | nodes.first.roles.must_equal %w[gray small] 151 | nodes.first.id.must_equal 'git' 152 | nodes.first.content.must_equal 'Git' 153 | end 154 | it 'should parse a escaped unconstrained emphasis chars with role' do 155 | ast = ::Asciidoctor::InlineParser.parse %(#{BACKSLASH}[gray]__Git__Hub) 156 | ast.text_value.must_equal %(#{BACKSLASH}[gray]__Git__Hub) 157 | nodes = find_by (node_type_must_be 'UnconstrainedEmphasis'), ast 158 | nodes.size.must_equal 1 159 | nodes.first.content.must_equal 'Git' 160 | end 161 | it 'should parse a constrained monospaced code snippet' do 162 | ast = ::Asciidoctor::InlineParser.parse '`var i = \'abcd\';`' 163 | ast.text_value.must_equal '`var i = \'abcd\';`' 164 | nodes = find_by (node_type_must_be 'Monospaced'), ast 165 | nodes.size.must_equal 1 166 | nodes.first.text_value.must_equal '`var i = \'abcd\';`' 167 | end 168 | it 'should parse a constrained superscript single letter' do 169 | ast = ::Asciidoctor::InlineParser.parse 'E = mc^2^' 170 | ast.text_value.must_equal 'E = mc^2^' 171 | nodes = find_by (node_type_must_be 'Superscript'), ast 172 | nodes.size.must_equal 1 173 | nodes.first.text_value.must_equal '^2^' 174 | end 175 | it 'should parse a constrained subscript single letter' do 176 | ast = ::Asciidoctor::InlineParser.parse '~a~' 177 | ast.text_value.must_equal '~a~' 178 | nodes = find_by (node_type_must_be 'Subscript'), ast 179 | nodes.size.must_equal 1 180 | nodes.first.text_value.must_equal '~a~' 181 | end 182 | it 'should parse a constrained monospaced string that contains /* and */' do 183 | ast = ::Asciidoctor::InlineParser.parse 'Use `/*` and `*/` for multiline comments.' 184 | ast.text_value.must_equal 'Use `/*` and `*/` for multiline comments.' 185 | nodes = find_by (node_type_must_be 'Monospaced'), ast 186 | nodes.size.must_equal 2 187 | nodes[0].text_value.must_equal '`/*`' 188 | nodes[1].text_value.must_equal '`*/`' 189 | end 190 | it 'should parse a nested quoted string (emphasis text inside a bold text)' do 191 | ast = ::Asciidoctor::InlineParser.parse 'The next *few words are _really_ important!*' 192 | ast.text_value.must_equal 'The next *few words are _really_ important!*' 193 | strong_nodes = find_by (node_type_must_be 'Strong'), ast 194 | strong_nodes.size.must_equal 1 195 | strong_nodes.first.text_value.must_equal '*few words are _really_ important!*' 196 | emphasis_nodes = find_by (node_type_must_be 'Emphasis'), ast 197 | emphasis_nodes.size.must_equal 1 198 | end 199 | it 'should parse a constrained emphasis string that contains numbers and the percent symbol' do 200 | ast = ::Asciidoctor::InlineParser.parse 'The parser is working _99%_ of the time' 201 | ast.text_value.must_equal 'The parser is working _99%_ of the time' 202 | nodes = find_by (node_type_must_be 'Emphasis'), ast 203 | nodes.size.must_equal 1 204 | nodes.first.text_value.must_equal '_99%_' 205 | nodes.first.content.must_equal '99%' 206 | end 207 | it 'should parse a constrained strong string that contains an underscore symbol' do 208 | ast = ::Asciidoctor::InlineParser.parse '*_id* word_' 209 | ast.text_value.must_equal '*_id* word_' 210 | emphasis_nodes = find_by (node_type_must_be 'Emphasis'), ast 211 | emphasis_nodes.size.must_equal 0 212 | strong_nodes = find_by (node_type_must_be 'Strong'), ast 213 | strong_nodes.size.must_equal 1 214 | strong_nodes.first.content.must_equal '_id' 215 | end 216 | it 'should parse an escaped constrained strong string' do 217 | ast = ::Asciidoctor::InlineParser.parse 'Escaped star symbol will not produce \*bold*.' 218 | ast.text_value.must_equal 'Escaped star symbol will not produce \*bold*.' 219 | nodes = find_by (node_type_must_be 'Strong'), ast 220 | nodes.size.must_equal 0 221 | end 222 | it 'should parse a deep nested quoted string' do 223 | ast = ::Asciidoctor::InlineParser.parse '*Deep _nested `#quoted#` ^text^_*' 224 | ast.text_value.must_equal '*Deep _nested `#quoted#` ^text^_*' 225 | strong_nodes = find_by (node_type_must_be 'Strong'), ast 226 | strong_nodes.size.must_equal 1 227 | strong_nodes.first.content.must_equal 'Deep _nested `#quoted#` ^text^_' 228 | emphasis_nodes = find_by (node_type_must_be 'Emphasis'), ast 229 | emphasis_nodes.size.must_equal 1 230 | emphasis_nodes.first.content.must_equal 'nested `#quoted#` ^text^' 231 | monospaced_nodes = find_by (node_type_must_be 'Monospaced'), ast 232 | monospaced_nodes.size.must_equal 1 233 | monospaced_nodes.first.content.must_equal '#quoted#' 234 | mark_nodes = find_by (node_type_must_be 'Mark'), ast 235 | mark_nodes.size.must_equal 1 236 | mark_nodes.first.content.must_equal 'quoted' 237 | superscript_nodes = find_by (node_type_must_be 'Superscript'), ast 238 | superscript_nodes.size.must_equal 1 239 | superscript_nodes.first.content.must_equal 'text' 240 | end 241 | it 'should parse an unconstrained strong string with a trailing semi-colon' do 242 | ast = ::Asciidoctor::InlineParser.parse '©__the authors__' 243 | ast.text_value.must_equal '©__the authors__' 244 | nodes = find_by (node_type_must_be 'UnconstrainedEmphasis'), ast 245 | nodes.size.must_equal 1 246 | nodes.first.text_value.must_equal '__the authors__' 247 | nodes.first.content.must_equal 'the authors' 248 | end 249 | it 'should parse a constrained monospaced code surrounded by parenthesis' do 250 | ast = ::Asciidoctor::InlineParser.parse 'Strong Importance Element (``)' 251 | ast.text_value.must_equal 'Strong Importance Element (``)' 252 | end 253 | end 254 | -------------------------------------------------------------------------------- /lib/asciidoctor/inline_parser/asciidoctor_image_grammar.rb: -------------------------------------------------------------------------------- 1 | # Autogenerated from a Treetop grammar. Edits may be lost. 2 | 3 | 4 | module AsciidoctorImageGrammar 5 | include Treetop::Runtime 6 | 7 | def root 8 | @root ||= :image 9 | end 10 | 11 | module Image0 12 | def image_target 13 | elements[1] 14 | end 15 | 16 | def image_attrs 17 | elements[2] 18 | end 19 | end 20 | 21 | def _nt_image 22 | start_index = index 23 | if node_cache[:image].has_key?(index) 24 | cached = node_cache[:image][index] 25 | if cached 26 | node_cache[:image][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 27 | @index = cached.interval.end 28 | end 29 | return cached 30 | end 31 | 32 | i0, s0 = index, [] 33 | if (match_len = has_terminal?('image:', false, index)) 34 | r1 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 35 | @index += match_len 36 | else 37 | terminal_parse_failure('\'image:\'') 38 | r1 = nil 39 | end 40 | s0 << r1 41 | if r1 42 | r2 = _nt_image_target 43 | s0 << r2 44 | if r2 45 | r3 = _nt_image_attrs 46 | s0 << r3 47 | end 48 | end 49 | if s0.last 50 | r0 = instantiate_node(Image,input, i0...index, s0) 51 | r0.extend(Image0) 52 | else 53 | @index = i0 54 | r0 = nil 55 | end 56 | 57 | node_cache[:image][start_index] = r0 58 | 59 | r0 60 | end 61 | 62 | def _nt_image_target 63 | start_index = index 64 | if node_cache[:image_target].has_key?(index) 65 | cached = node_cache[:image_target][index] 66 | if cached 67 | node_cache[:image_target][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 68 | @index = cached.interval.end 69 | end 70 | return cached 71 | end 72 | 73 | s0, i0 = [], index 74 | loop do 75 | if has_terminal?(@regexps[gr = '\A[^\\[]'] ||= Regexp.new(gr), :regexp, index) 76 | r1 = true 77 | @index += 1 78 | else 79 | terminal_parse_failure('[^\\[]') 80 | r1 = nil 81 | end 82 | if r1 83 | s0 << r1 84 | else 85 | break 86 | end 87 | end 88 | if s0.empty? 89 | @index = i0 90 | r0 = nil 91 | else 92 | r0 = instantiate_node(ImageTarget,input, i0...index, s0) 93 | end 94 | 95 | node_cache[:image_target][start_index] = r0 96 | 97 | r0 98 | end 99 | 100 | module ImageAttrs0 101 | def image_attrs_content 102 | elements[1] 103 | end 104 | 105 | end 106 | 107 | def _nt_image_attrs 108 | start_index = index 109 | if node_cache[:image_attrs].has_key?(index) 110 | cached = node_cache[:image_attrs][index] 111 | if cached 112 | node_cache[:image_attrs][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 113 | @index = cached.interval.end 114 | end 115 | return cached 116 | end 117 | 118 | i0, s0 = index, [] 119 | if (match_len = has_terminal?('[', false, index)) 120 | r1 = true 121 | @index += match_len 122 | else 123 | terminal_parse_failure('\'[\'') 124 | r1 = nil 125 | end 126 | s0 << r1 127 | if r1 128 | r2 = _nt_image_attrs_content 129 | s0 << r2 130 | if r2 131 | if (match_len = has_terminal?(']', false, index)) 132 | r3 = true 133 | @index += match_len 134 | else 135 | terminal_parse_failure('\']\'') 136 | r3 = nil 137 | end 138 | s0 << r3 139 | end 140 | end 141 | if s0.last 142 | r0 = instantiate_node(ImageAttributes,input, i0...index, s0) 143 | r0.extend(ImageAttrs0) 144 | else 145 | @index = i0 146 | r0 = nil 147 | end 148 | 149 | node_cache[:image_attrs][start_index] = r0 150 | 151 | r0 152 | end 153 | 154 | def _nt_image_attrs_content 155 | start_index = index 156 | if node_cache[:image_attrs_content].has_key?(index) 157 | cached = node_cache[:image_attrs_content][index] 158 | if cached 159 | node_cache[:image_attrs_content][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 160 | @index = cached.interval.end 161 | end 162 | return cached 163 | end 164 | 165 | s0, i0 = [], index 166 | loop do 167 | i1 = index 168 | r2 = _nt_image_attr_text_protected 169 | if r2 170 | r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true 171 | r1 = r2 172 | else 173 | if (match_len = has_terminal?(',', false, index)) 174 | r3 = true 175 | @index += match_len 176 | else 177 | terminal_parse_failure('\',\'') 178 | r3 = nil 179 | end 180 | if r3 181 | r3 = SyntaxNode.new(input, (index-1)...index) if r3 == true 182 | r1 = r3 183 | else 184 | if (match_len = has_terminal?(' ', false, index)) 185 | r4 = true 186 | @index += match_len 187 | else 188 | terminal_parse_failure('\' \'') 189 | r4 = nil 190 | end 191 | if r4 192 | r4 = SyntaxNode.new(input, (index-1)...index) if r4 == true 193 | r1 = r4 194 | else 195 | r5 = _nt_image_named_attr 196 | if r5 197 | r5 = SyntaxNode.new(input, (index-1)...index) if r5 == true 198 | r1 = r5 199 | else 200 | r6 = _nt_image_attr_text_unprotected 201 | if r6 202 | r6 = SyntaxNode.new(input, (index-1)...index) if r6 == true 203 | r1 = r6 204 | else 205 | @index = i1 206 | r1 = nil 207 | end 208 | end 209 | end 210 | end 211 | end 212 | if r1 213 | s0 << r1 214 | else 215 | break 216 | end 217 | end 218 | r0 = instantiate_node(ImageAttributesContent,input, i0...index, s0) 219 | 220 | node_cache[:image_attrs_content][start_index] = r0 221 | 222 | r0 223 | end 224 | 225 | def _nt_image_attr_text_unprotected 226 | start_index = index 227 | if node_cache[:image_attr_text_unprotected].has_key?(index) 228 | cached = node_cache[:image_attr_text_unprotected][index] 229 | if cached 230 | node_cache[:image_attr_text_unprotected][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 231 | @index = cached.interval.end 232 | end 233 | return cached 234 | end 235 | 236 | s0, i0 = [], index 237 | loop do 238 | if has_terminal?(@regexps[gr = '\A[^,\\]]'] ||= Regexp.new(gr), :regexp, index) 239 | r1 = true 240 | @index += 1 241 | else 242 | terminal_parse_failure('[^,\\]]') 243 | r1 = nil 244 | end 245 | if r1 246 | s0 << r1 247 | else 248 | break 249 | end 250 | end 251 | if s0.empty? 252 | @index = i0 253 | r0 = nil 254 | else 255 | r0 = instantiate_node(ImageAttributeValue,input, i0...index, s0) 256 | end 257 | 258 | node_cache[:image_attr_text_unprotected][start_index] = r0 259 | 260 | r0 261 | end 262 | 263 | module ImageAttrTextProtected0 264 | def image_text_protected 265 | elements[1] 266 | end 267 | 268 | end 269 | 270 | def _nt_image_attr_text_protected 271 | start_index = index 272 | if node_cache[:image_attr_text_protected].has_key?(index) 273 | cached = node_cache[:image_attr_text_protected][index] 274 | if cached 275 | node_cache[:image_attr_text_protected][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 276 | @index = cached.interval.end 277 | end 278 | return cached 279 | end 280 | 281 | i0, s0 = index, [] 282 | if (match_len = has_terminal?('"', false, index)) 283 | r1 = true 284 | @index += match_len 285 | else 286 | terminal_parse_failure('\'"\'') 287 | r1 = nil 288 | end 289 | s0 << r1 290 | if r1 291 | r2 = _nt_image_text_protected 292 | s0 << r2 293 | if r2 294 | if (match_len = has_terminal?('"', false, index)) 295 | r3 = true 296 | @index += match_len 297 | else 298 | terminal_parse_failure('\'"\'') 299 | r3 = nil 300 | end 301 | s0 << r3 302 | end 303 | end 304 | if s0.last 305 | r0 = instantiate_node(ImageAttributeValueProtected,input, i0...index, s0) 306 | r0.extend(ImageAttrTextProtected0) 307 | else 308 | @index = i0 309 | r0 = nil 310 | end 311 | 312 | node_cache[:image_attr_text_protected][start_index] = r0 313 | 314 | r0 315 | end 316 | 317 | def _nt_image_text_protected 318 | start_index = index 319 | if node_cache[:image_text_protected].has_key?(index) 320 | cached = node_cache[:image_text_protected][index] 321 | if cached 322 | node_cache[:image_text_protected][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 323 | @index = cached.interval.end 324 | end 325 | return cached 326 | end 327 | 328 | s0, i0 = [], index 329 | loop do 330 | if has_terminal?(@regexps[gr = '\A[^"]'] ||= Regexp.new(gr), :regexp, index) 331 | r1 = true 332 | @index += 1 333 | else 334 | terminal_parse_failure('[^"]') 335 | r1 = nil 336 | end 337 | if r1 338 | s0 << r1 339 | else 340 | break 341 | end 342 | end 343 | if s0.empty? 344 | @index = i0 345 | r0 = nil 346 | else 347 | r0 = instantiate_node(ImageAttributeValue,input, i0...index, s0) 348 | end 349 | 350 | node_cache[:image_text_protected][start_index] = r0 351 | 352 | r0 353 | end 354 | 355 | module ImageNamedAttr0 356 | def image_named_attr_key 357 | elements[0] 358 | end 359 | 360 | def image_named_attr_value 361 | elements[2] 362 | end 363 | end 364 | 365 | def _nt_image_named_attr 366 | start_index = index 367 | if node_cache[:image_named_attr].has_key?(index) 368 | cached = node_cache[:image_named_attr][index] 369 | if cached 370 | node_cache[:image_named_attr][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 371 | @index = cached.interval.end 372 | end 373 | return cached 374 | end 375 | 376 | i0, s0 = index, [] 377 | r1 = _nt_image_named_attr_key 378 | s0 << r1 379 | if r1 380 | if (match_len = has_terminal?('=', false, index)) 381 | r2 = true 382 | @index += match_len 383 | else 384 | terminal_parse_failure('\'=\'') 385 | r2 = nil 386 | end 387 | s0 << r2 388 | if r2 389 | r3 = _nt_image_named_attr_value 390 | s0 << r3 391 | end 392 | end 393 | if s0.last 394 | r0 = instantiate_node(ImageNamedAttribute,input, i0...index, s0) 395 | r0.extend(ImageNamedAttr0) 396 | else 397 | @index = i0 398 | r0 = nil 399 | end 400 | 401 | node_cache[:image_named_attr][start_index] = r0 402 | 403 | r0 404 | end 405 | 406 | def _nt_image_named_attr_key 407 | start_index = index 408 | if node_cache[:image_named_attr_key].has_key?(index) 409 | cached = node_cache[:image_named_attr_key][index] 410 | if cached 411 | node_cache[:image_named_attr_key][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 412 | @index = cached.interval.end 413 | end 414 | return cached 415 | end 416 | 417 | s0, i0 = [], index 418 | loop do 419 | if has_terminal?(@regexps[gr = '\A[^,=]'] ||= Regexp.new(gr), :regexp, index) 420 | r1 = true 421 | @index += 1 422 | else 423 | terminal_parse_failure('[^,=]') 424 | r1 = nil 425 | end 426 | if r1 427 | s0 << r1 428 | else 429 | break 430 | end 431 | end 432 | if s0.empty? 433 | @index = i0 434 | r0 = nil 435 | else 436 | r0 = instantiate_node(ImageNamedAttributeKey,input, i0...index, s0) 437 | end 438 | 439 | node_cache[:image_named_attr_key][start_index] = r0 440 | 441 | r0 442 | end 443 | 444 | def _nt_image_named_attr_value 445 | start_index = index 446 | if node_cache[:image_named_attr_value].has_key?(index) 447 | cached = node_cache[:image_named_attr_value][index] 448 | if cached 449 | node_cache[:image_named_attr_value][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 450 | @index = cached.interval.end 451 | end 452 | return cached 453 | end 454 | 455 | i0 = index 456 | r1 = _nt_image_text_protected 457 | if r1 458 | r1 = SyntaxNode.new(input, (index-1)...index) if r1 == true 459 | r0 = r1 460 | else 461 | r2 = _nt_image_attr_text_protected 462 | if r2 463 | r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true 464 | r0 = r2 465 | else 466 | @index = i0 467 | r0 = nil 468 | end 469 | end 470 | 471 | node_cache[:image_named_attr_value][start_index] = r0 472 | 473 | r0 474 | end 475 | 476 | def _nt_image_attr_value_unprotected 477 | start_index = index 478 | if node_cache[:image_attr_value_unprotected].has_key?(index) 479 | cached = node_cache[:image_attr_value_unprotected][index] 480 | if cached 481 | node_cache[:image_attr_value_unprotected][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 482 | @index = cached.interval.end 483 | end 484 | return cached 485 | end 486 | 487 | s0, i0 = [], index 488 | loop do 489 | if has_terminal?(@regexps[gr = '\A[^,\\]]'] ||= Regexp.new(gr), :regexp, index) 490 | r1 = true 491 | @index += 1 492 | else 493 | terminal_parse_failure('[^,\\]]') 494 | r1 = nil 495 | end 496 | if r1 497 | s0 << r1 498 | else 499 | break 500 | end 501 | end 502 | if s0.empty? 503 | @index = i0 504 | r0 = nil 505 | else 506 | r0 = instantiate_node(ImageAttributeValue,input, i0...index, s0) 507 | end 508 | 509 | node_cache[:image_attr_value_unprotected][start_index] = r0 510 | 511 | r0 512 | end 513 | 514 | end 515 | 516 | class AsciidoctorImageGrammarParser < Treetop::Runtime::CompiledParser 517 | include AsciidoctorImageGrammar 518 | end 519 | 520 | -------------------------------------------------------------------------------- /lib/asciidoctor/inline_parser/asciidoctor_email_grammar.rb: -------------------------------------------------------------------------------- 1 | # Autogenerated from a Treetop grammar. Edits may be lost. 2 | 3 | 4 | module AsciidoctorEmailGrammar 5 | include Treetop::Runtime 6 | 7 | def root 8 | @root ||= :escaped_implicit_email 9 | end 10 | 11 | module EscapedImplicitEmail0 12 | def implicit_email 13 | elements[1] 14 | end 15 | end 16 | 17 | def _nt_escaped_implicit_email 18 | start_index = index 19 | if node_cache[:escaped_implicit_email].has_key?(index) 20 | cached = node_cache[:escaped_implicit_email][index] 21 | if cached 22 | node_cache[:escaped_implicit_email][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 23 | @index = cached.interval.end 24 | end 25 | return cached 26 | end 27 | 28 | i0, s0 = index, [] 29 | if (match_len = has_terminal?('\\', false, index)) 30 | r1 = true 31 | @index += match_len 32 | else 33 | terminal_parse_failure('\'\\\\\'') 34 | r1 = nil 35 | end 36 | s0 << r1 37 | if r1 38 | r2 = _nt_implicit_email 39 | s0 << r2 40 | end 41 | if s0.last 42 | r0 = instantiate_node(EscapedEmail,input, i0...index, s0) 43 | r0.extend(EscapedImplicitEmail0) 44 | else 45 | @index = i0 46 | r0 = nil 47 | end 48 | 49 | node_cache[:escaped_implicit_email][start_index] = r0 50 | 51 | r0 52 | end 53 | 54 | module EscapedExplicitEmail0 55 | def explicit_email 56 | elements[1] 57 | end 58 | end 59 | 60 | def _nt_escaped_explicit_email 61 | start_index = index 62 | if node_cache[:escaped_explicit_email].has_key?(index) 63 | cached = node_cache[:escaped_explicit_email][index] 64 | if cached 65 | node_cache[:escaped_explicit_email][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 66 | @index = cached.interval.end 67 | end 68 | return cached 69 | end 70 | 71 | i0, s0 = index, [] 72 | if (match_len = has_terminal?('\\', false, index)) 73 | r1 = true 74 | @index += match_len 75 | else 76 | terminal_parse_failure('\'\\\\\'') 77 | r1 = nil 78 | end 79 | s0 << r1 80 | if r1 81 | r2 = _nt_explicit_email 82 | s0 << r2 83 | end 84 | if s0.last 85 | r0 = instantiate_node(EscapedEmail,input, i0...index, s0) 86 | r0.extend(EscapedExplicitEmail0) 87 | else 88 | @index = i0 89 | r0 = nil 90 | end 91 | 92 | node_cache[:escaped_explicit_email][start_index] = r0 93 | 94 | r0 95 | end 96 | 97 | module ExplicitEmail0 98 | def implicit_email 99 | elements[1] 100 | end 101 | 102 | def email_attrs 103 | elements[2] 104 | end 105 | end 106 | 107 | def _nt_explicit_email 108 | start_index = index 109 | if node_cache[:explicit_email].has_key?(index) 110 | cached = node_cache[:explicit_email][index] 111 | if cached 112 | node_cache[:explicit_email][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 113 | @index = cached.interval.end 114 | end 115 | return cached 116 | end 117 | 118 | i0, s0 = index, [] 119 | if (match_len = has_terminal?('mailto:', false, index)) 120 | r1 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 121 | @index += match_len 122 | else 123 | terminal_parse_failure('\'mailto:\'') 124 | r1 = nil 125 | end 126 | s0 << r1 127 | if r1 128 | r2 = _nt_implicit_email 129 | s0 << r2 130 | if r2 131 | r3 = _nt_email_attrs 132 | s0 << r3 133 | end 134 | end 135 | if s0.last 136 | r0 = instantiate_node(EmailMacro,input, i0...index, s0) 137 | r0.extend(ExplicitEmail0) 138 | else 139 | @index = i0 140 | r0 = nil 141 | end 142 | 143 | node_cache[:explicit_email][start_index] = r0 144 | 145 | r0 146 | end 147 | 148 | module ImplicitEmail0 149 | def email_name 150 | elements[0] 151 | end 152 | 153 | def email_domain 154 | elements[2] 155 | end 156 | end 157 | 158 | def _nt_implicit_email 159 | start_index = index 160 | if node_cache[:implicit_email].has_key?(index) 161 | cached = node_cache[:implicit_email][index] 162 | if cached 163 | node_cache[:implicit_email][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 164 | @index = cached.interval.end 165 | end 166 | return cached 167 | end 168 | 169 | i0, s0 = index, [] 170 | r1 = _nt_email_name 171 | s0 << r1 172 | if r1 173 | if (match_len = has_terminal?('@', false, index)) 174 | r2 = true 175 | @index += match_len 176 | else 177 | terminal_parse_failure('\'@\'') 178 | r2 = nil 179 | end 180 | s0 << r2 181 | if r2 182 | r3 = _nt_email_domain 183 | s0 << r3 184 | end 185 | end 186 | if s0.last 187 | r0 = instantiate_node(Email,input, i0...index, s0) 188 | r0.extend(ImplicitEmail0) 189 | else 190 | @index = i0 191 | r0 = nil 192 | end 193 | 194 | node_cache[:implicit_email][start_index] = r0 195 | 196 | r0 197 | end 198 | 199 | def _nt_email_name 200 | start_index = index 201 | if node_cache[:email_name].has_key?(index) 202 | cached = node_cache[:email_name][index] 203 | if cached 204 | node_cache[:email_name][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 205 | @index = cached.interval.end 206 | end 207 | return cached 208 | end 209 | 210 | s0, i0 = [], index 211 | loop do 212 | i1 = index 213 | r2 = _nt_word 214 | if r2 215 | r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true 216 | r1 = r2 217 | else 218 | if has_terminal?(@regexps[gr = '\A[\\.%+\\-]'] ||= Regexp.new(gr), :regexp, index) 219 | r3 = true 220 | @index += 1 221 | else 222 | terminal_parse_failure('[\\.%+\\-]') 223 | r3 = nil 224 | end 225 | if r3 226 | r3 = SyntaxNode.new(input, (index-1)...index) if r3 == true 227 | r1 = r3 228 | else 229 | @index = i1 230 | r1 = nil 231 | end 232 | end 233 | if r1 234 | s0 << r1 235 | else 236 | break 237 | end 238 | end 239 | if s0.empty? 240 | @index = i0 241 | r0 = nil 242 | else 243 | r0 = instantiate_node(SyntaxNode,input, i0...index, s0) 244 | end 245 | 246 | node_cache[:email_name][start_index] = r0 247 | 248 | r0 249 | end 250 | 251 | module EmailDomain0 252 | def word 253 | elements[0] 254 | end 255 | 256 | end 257 | 258 | def _nt_email_domain 259 | start_index = index 260 | if node_cache[:email_domain].has_key?(index) 261 | cached = node_cache[:email_domain][index] 262 | if cached 263 | node_cache[:email_domain][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 264 | @index = cached.interval.end 265 | end 266 | return cached 267 | end 268 | 269 | i0, s0 = index, [] 270 | r1 = _nt_word 271 | s0 << r1 272 | if r1 273 | s2, i2 = [], index 274 | loop do 275 | i3 = index 276 | r4 = _nt_email_tld 277 | if r4 278 | r4 = SyntaxNode.new(input, (index-1)...index) if r4 == true 279 | r3 = r4 280 | else 281 | r5 = _nt_word 282 | if r5 283 | r5 = SyntaxNode.new(input, (index-1)...index) if r5 == true 284 | r3 = r5 285 | else 286 | @index = i3 287 | r3 = nil 288 | end 289 | end 290 | if r3 291 | s2 << r3 292 | else 293 | break 294 | end 295 | end 296 | if s2.empty? 297 | @index = i2 298 | r2 = nil 299 | else 300 | r2 = instantiate_node(SyntaxNode,input, i2...index, s2) 301 | end 302 | s0 << r2 303 | end 304 | if s0.last 305 | r0 = instantiate_node(SyntaxNode,input, i0...index, s0) 306 | r0.extend(EmailDomain0) 307 | else 308 | @index = i0 309 | r0 = nil 310 | end 311 | 312 | node_cache[:email_domain][start_index] = r0 313 | 314 | r0 315 | end 316 | 317 | module EmailTld0 318 | end 319 | 320 | def _nt_email_tld 321 | start_index = index 322 | if node_cache[:email_tld].has_key?(index) 323 | cached = node_cache[:email_tld][index] 324 | if cached 325 | node_cache[:email_tld][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 326 | @index = cached.interval.end 327 | end 328 | return cached 329 | end 330 | 331 | i0, s0 = index, [] 332 | if (match_len = has_terminal?('.', false, index)) 333 | r1 = true 334 | @index += match_len 335 | else 336 | terminal_parse_failure('\'.\'') 337 | r1 = nil 338 | end 339 | s0 << r1 340 | if r1 341 | s2, i2 = [], index 342 | loop do 343 | r3 = _nt_alpha 344 | if r3 345 | s2 << r3 346 | else 347 | break 348 | end 349 | if s2.size == 4 350 | break 351 | end 352 | end 353 | if s2.size < 1 354 | @index = i2 355 | r2 = nil 356 | else 357 | if s2.size < 4 358 | @terminal_failures.pop 359 | end 360 | r2 = instantiate_node(SyntaxNode,input, i2...index, s2) 361 | end 362 | s0 << r2 363 | end 364 | if s0.last 365 | r0 = instantiate_node(SyntaxNode,input, i0...index, s0) 366 | r0.extend(EmailTld0) 367 | else 368 | @index = i0 369 | r0 = nil 370 | end 371 | 372 | node_cache[:email_tld][start_index] = r0 373 | 374 | r0 375 | end 376 | 377 | module EmailAttrs0 378 | def email_attrs_content 379 | elements[1] 380 | end 381 | 382 | end 383 | 384 | def _nt_email_attrs 385 | start_index = index 386 | if node_cache[:email_attrs].has_key?(index) 387 | cached = node_cache[:email_attrs][index] 388 | if cached 389 | node_cache[:email_attrs][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 390 | @index = cached.interval.end 391 | end 392 | return cached 393 | end 394 | 395 | i0, s0 = index, [] 396 | if (match_len = has_terminal?('[', false, index)) 397 | r1 = true 398 | @index += match_len 399 | else 400 | terminal_parse_failure('\'[\'') 401 | r1 = nil 402 | end 403 | s0 << r1 404 | if r1 405 | r2 = _nt_email_attrs_content 406 | s0 << r2 407 | if r2 408 | if (match_len = has_terminal?(']', false, index)) 409 | r3 = true 410 | @index += match_len 411 | else 412 | terminal_parse_failure('\']\'') 413 | r3 = nil 414 | end 415 | s0 << r3 416 | end 417 | end 418 | if s0.last 419 | r0 = instantiate_node(EmailAttributes,input, i0...index, s0) 420 | r0.extend(EmailAttrs0) 421 | else 422 | @index = i0 423 | r0 = nil 424 | end 425 | 426 | node_cache[:email_attrs][start_index] = r0 427 | 428 | r0 429 | end 430 | 431 | def _nt_email_attrs_content 432 | start_index = index 433 | if node_cache[:email_attrs_content].has_key?(index) 434 | cached = node_cache[:email_attrs_content][index] 435 | if cached 436 | node_cache[:email_attrs_content][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 437 | @index = cached.interval.end 438 | end 439 | return cached 440 | end 441 | 442 | s0, i0 = [], index 443 | loop do 444 | i1 = index 445 | r2 = _nt_email_attr_text_protected 446 | if r2 447 | r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true 448 | r1 = r2 449 | else 450 | if (match_len = has_terminal?(',', false, index)) 451 | r3 = true 452 | @index += match_len 453 | else 454 | terminal_parse_failure('\',\'') 455 | r3 = nil 456 | end 457 | if r3 458 | r3 = SyntaxNode.new(input, (index-1)...index) if r3 == true 459 | r1 = r3 460 | else 461 | if (match_len = has_terminal?(' ', false, index)) 462 | r4 = true 463 | @index += match_len 464 | else 465 | terminal_parse_failure('\' \'') 466 | r4 = nil 467 | end 468 | if r4 469 | r4 = SyntaxNode.new(input, (index-1)...index) if r4 == true 470 | r1 = r4 471 | else 472 | r5 = _nt_email_attr_text_unprotected 473 | if r5 474 | r5 = SyntaxNode.new(input, (index-1)...index) if r5 == true 475 | r1 = r5 476 | else 477 | @index = i1 478 | r1 = nil 479 | end 480 | end 481 | end 482 | end 483 | if r1 484 | s0 << r1 485 | else 486 | break 487 | end 488 | end 489 | r0 = instantiate_node(EmailAttributesContent,input, i0...index, s0) 490 | 491 | node_cache[:email_attrs_content][start_index] = r0 492 | 493 | r0 494 | end 495 | 496 | def _nt_email_attr_text_unprotected 497 | start_index = index 498 | if node_cache[:email_attr_text_unprotected].has_key?(index) 499 | cached = node_cache[:email_attr_text_unprotected][index] 500 | if cached 501 | node_cache[:email_attr_text_unprotected][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 502 | @index = cached.interval.end 503 | end 504 | return cached 505 | end 506 | 507 | s0, i0 = [], index 508 | loop do 509 | if has_terminal?(@regexps[gr = '\A[^,\\]]'] ||= Regexp.new(gr), :regexp, index) 510 | r1 = true 511 | @index += 1 512 | else 513 | terminal_parse_failure('[^,\\]]') 514 | r1 = nil 515 | end 516 | if r1 517 | s0 << r1 518 | else 519 | break 520 | end 521 | end 522 | if s0.empty? 523 | @index = i0 524 | r0 = nil 525 | else 526 | r0 = instantiate_node(EmailText,input, i0...index, s0) 527 | end 528 | 529 | node_cache[:email_attr_text_unprotected][start_index] = r0 530 | 531 | r0 532 | end 533 | 534 | module EmailAttrTextProtected0 535 | def email_text_protected 536 | elements[1] 537 | end 538 | 539 | end 540 | 541 | def _nt_email_attr_text_protected 542 | start_index = index 543 | if node_cache[:email_attr_text_protected].has_key?(index) 544 | cached = node_cache[:email_attr_text_protected][index] 545 | if cached 546 | node_cache[:email_attr_text_protected][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 547 | @index = cached.interval.end 548 | end 549 | return cached 550 | end 551 | 552 | i0, s0 = index, [] 553 | if (match_len = has_terminal?('"', false, index)) 554 | r1 = true 555 | @index += match_len 556 | else 557 | terminal_parse_failure('\'"\'') 558 | r1 = nil 559 | end 560 | s0 << r1 561 | if r1 562 | r2 = _nt_email_text_protected 563 | s0 << r2 564 | if r2 565 | if (match_len = has_terminal?('"', false, index)) 566 | r3 = true 567 | @index += match_len 568 | else 569 | terminal_parse_failure('\'"\'') 570 | r3 = nil 571 | end 572 | s0 << r3 573 | end 574 | end 575 | if s0.last 576 | r0 = instantiate_node(EmailTextProtected,input, i0...index, s0) 577 | r0.extend(EmailAttrTextProtected0) 578 | else 579 | @index = i0 580 | r0 = nil 581 | end 582 | 583 | node_cache[:email_attr_text_protected][start_index] = r0 584 | 585 | r0 586 | end 587 | 588 | def _nt_email_text_protected 589 | start_index = index 590 | if node_cache[:email_text_protected].has_key?(index) 591 | cached = node_cache[:email_text_protected][index] 592 | if cached 593 | node_cache[:email_text_protected][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 594 | @index = cached.interval.end 595 | end 596 | return cached 597 | end 598 | 599 | s0, i0 = [], index 600 | loop do 601 | if has_terminal?(@regexps[gr = '\A[^"]'] ||= Regexp.new(gr), :regexp, index) 602 | r1 = true 603 | @index += 1 604 | else 605 | terminal_parse_failure('[^"]') 606 | r1 = nil 607 | end 608 | if r1 609 | s0 << r1 610 | else 611 | break 612 | end 613 | end 614 | if s0.empty? 615 | @index = i0 616 | r0 = nil 617 | else 618 | r0 = instantiate_node(EmailText,input, i0...index, s0) 619 | end 620 | 621 | node_cache[:email_text_protected][start_index] = r0 622 | 623 | r0 624 | end 625 | 626 | end 627 | 628 | class AsciidoctorEmailGrammarParser < Treetop::Runtime::CompiledParser 629 | include AsciidoctorEmailGrammar 630 | end 631 | 632 | -------------------------------------------------------------------------------- /lib/asciidoctor/inline_parser/asciidoctor_passthrough_grammar.rb: -------------------------------------------------------------------------------- 1 | # Autogenerated from a Treetop grammar. Edits may be lost. 2 | 3 | 4 | module AsciidoctorPassthroughGrammar 5 | include Treetop::Runtime 6 | 7 | def root 8 | @root ||= :escaped_passthrough_inline_macro 9 | end 10 | 11 | module EscapedPassthroughInlineMacro0 12 | def passthrough_inline_macro 13 | elements[1] 14 | end 15 | end 16 | 17 | def _nt_escaped_passthrough_inline_macro 18 | start_index = index 19 | if node_cache[:escaped_passthrough_inline_macro].has_key?(index) 20 | cached = node_cache[:escaped_passthrough_inline_macro][index] 21 | if cached 22 | node_cache[:escaped_passthrough_inline_macro][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 23 | @index = cached.interval.end 24 | end 25 | return cached 26 | end 27 | 28 | i0, s0 = index, [] 29 | if (match_len = has_terminal?('\\', false, index)) 30 | r1 = true 31 | @index += match_len 32 | else 33 | terminal_parse_failure('\'\\\\\'') 34 | r1 = nil 35 | end 36 | s0 << r1 37 | if r1 38 | r2 = _nt_passthrough_inline_macro 39 | s0 << r2 40 | end 41 | if s0.last 42 | r0 = instantiate_node(EscapedPassthroughInlineMacro,input, i0...index, s0) 43 | r0.extend(EscapedPassthroughInlineMacro0) 44 | else 45 | @index = i0 46 | r0 = nil 47 | end 48 | 49 | node_cache[:escaped_passthrough_inline_macro][start_index] = r0 50 | 51 | r0 52 | end 53 | 54 | module PassthroughInlineMacro0 55 | def passthrough_inline_macro_subs 56 | elements[1] 57 | end 58 | 59 | def passthrough_inline_macro_content 60 | elements[3] 61 | end 62 | 63 | end 64 | 65 | def _nt_passthrough_inline_macro 66 | start_index = index 67 | if node_cache[:passthrough_inline_macro].has_key?(index) 68 | cached = node_cache[:passthrough_inline_macro][index] 69 | if cached 70 | node_cache[:passthrough_inline_macro][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 71 | @index = cached.interval.end 72 | end 73 | return cached 74 | end 75 | 76 | i0, s0 = index, [] 77 | if (match_len = has_terminal?('pass:', false, index)) 78 | r1 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 79 | @index += match_len 80 | else 81 | terminal_parse_failure('\'pass:\'') 82 | r1 = nil 83 | end 84 | s0 << r1 85 | if r1 86 | r2 = _nt_passthrough_inline_macro_subs 87 | s0 << r2 88 | if r2 89 | if (match_len = has_terminal?('[', false, index)) 90 | r3 = true 91 | @index += match_len 92 | else 93 | terminal_parse_failure('\'[\'') 94 | r3 = nil 95 | end 96 | s0 << r3 97 | if r3 98 | r4 = _nt_passthrough_inline_macro_content 99 | s0 << r4 100 | if r4 101 | if (match_len = has_terminal?(']', false, index)) 102 | r5 = true 103 | @index += match_len 104 | else 105 | terminal_parse_failure('\']\'') 106 | r5 = nil 107 | end 108 | s0 << r5 109 | end 110 | end 111 | end 112 | end 113 | if s0.last 114 | r0 = instantiate_node(PassthroughInlineMacro,input, i0...index, s0) 115 | r0.extend(PassthroughInlineMacro0) 116 | else 117 | @index = i0 118 | r0 = nil 119 | end 120 | 121 | node_cache[:passthrough_inline_macro][start_index] = r0 122 | 123 | r0 124 | end 125 | 126 | def _nt_passthrough_inline_macro_subs 127 | start_index = index 128 | if node_cache[:passthrough_inline_macro_subs].has_key?(index) 129 | cached = node_cache[:passthrough_inline_macro_subs][index] 130 | if cached 131 | node_cache[:passthrough_inline_macro_subs][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 132 | @index = cached.interval.end 133 | end 134 | return cached 135 | end 136 | 137 | s0, i0 = [], index 138 | loop do 139 | i1 = index 140 | if (match_len = has_terminal?(',', false, index)) 141 | r2 = true 142 | @index += match_len 143 | else 144 | terminal_parse_failure('\',\'') 145 | r2 = nil 146 | end 147 | if r2 148 | r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true 149 | r1 = r2 150 | else 151 | r3 = _nt_passthrough_inline_macro_sub 152 | if r3 153 | r3 = SyntaxNode.new(input, (index-1)...index) if r3 == true 154 | r1 = r3 155 | else 156 | @index = i1 157 | r1 = nil 158 | end 159 | end 160 | if r1 161 | s0 << r1 162 | else 163 | break 164 | end 165 | end 166 | r0 = instantiate_node(PassthroughInlineMacroSubs,input, i0...index, s0) 167 | 168 | node_cache[:passthrough_inline_macro_subs][start_index] = r0 169 | 170 | r0 171 | end 172 | 173 | def _nt_passthrough_inline_macro_sub 174 | start_index = index 175 | if node_cache[:passthrough_inline_macro_sub].has_key?(index) 176 | cached = node_cache[:passthrough_inline_macro_sub][index] 177 | if cached 178 | node_cache[:passthrough_inline_macro_sub][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 179 | @index = cached.interval.end 180 | end 181 | return cached 182 | end 183 | 184 | s0, i0 = [], index 185 | loop do 186 | i1 = index 187 | if (match_len = has_terminal?('normal', false, index)) 188 | r2 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 189 | @index += match_len 190 | else 191 | terminal_parse_failure('\'normal\'') 192 | r2 = nil 193 | end 194 | if r2 195 | r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true 196 | r1 = r2 197 | else 198 | if (match_len = has_terminal?('none', false, index)) 199 | r3 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 200 | @index += match_len 201 | else 202 | terminal_parse_failure('\'none\'') 203 | r3 = nil 204 | end 205 | if r3 206 | r3 = SyntaxNode.new(input, (index-1)...index) if r3 == true 207 | r1 = r3 208 | else 209 | if (match_len = has_terminal?('specialcharacters', false, index)) 210 | r4 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 211 | @index += match_len 212 | else 213 | terminal_parse_failure('\'specialcharacters\'') 214 | r4 = nil 215 | end 216 | if r4 217 | r4 = SyntaxNode.new(input, (index-1)...index) if r4 == true 218 | r1 = r4 219 | else 220 | if (match_len = has_terminal?('specialchars', false, index)) 221 | r5 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 222 | @index += match_len 223 | else 224 | terminal_parse_failure('\'specialchars\'') 225 | r5 = nil 226 | end 227 | if r5 228 | r5 = SyntaxNode.new(input, (index-1)...index) if r5 == true 229 | r1 = r5 230 | else 231 | if (match_len = has_terminal?('quotes', false, index)) 232 | r6 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 233 | @index += match_len 234 | else 235 | terminal_parse_failure('\'quotes\'') 236 | r6 = nil 237 | end 238 | if r6 239 | r6 = SyntaxNode.new(input, (index-1)...index) if r6 == true 240 | r1 = r6 241 | else 242 | if (match_len = has_terminal?('attributes', false, index)) 243 | r7 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 244 | @index += match_len 245 | else 246 | terminal_parse_failure('\'attributes\'') 247 | r7 = nil 248 | end 249 | if r7 250 | r7 = SyntaxNode.new(input, (index-1)...index) if r7 == true 251 | r1 = r7 252 | else 253 | if (match_len = has_terminal?('replacements', false, index)) 254 | r8 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 255 | @index += match_len 256 | else 257 | terminal_parse_failure('\'replacements\'') 258 | r8 = nil 259 | end 260 | if r8 261 | r8 = SyntaxNode.new(input, (index-1)...index) if r8 == true 262 | r1 = r8 263 | else 264 | if (match_len = has_terminal?('macros', false, index)) 265 | r9 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 266 | @index += match_len 267 | else 268 | terminal_parse_failure('\'macros\'') 269 | r9 = nil 270 | end 271 | if r9 272 | r9 = SyntaxNode.new(input, (index-1)...index) if r9 == true 273 | r1 = r9 274 | else 275 | if (match_len = has_terminal?('post_replacements', false, index)) 276 | r10 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 277 | @index += match_len 278 | else 279 | terminal_parse_failure('\'post_replacements\'') 280 | r10 = nil 281 | end 282 | if r10 283 | r10 = SyntaxNode.new(input, (index-1)...index) if r10 == true 284 | r1 = r10 285 | else 286 | if (match_len = has_terminal?('verbatim', false, index)) 287 | r11 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 288 | @index += match_len 289 | else 290 | terminal_parse_failure('\'verbatim\'') 291 | r11 = nil 292 | end 293 | if r11 294 | r11 = SyntaxNode.new(input, (index-1)...index) if r11 == true 295 | r1 = r11 296 | else 297 | if (match_len = has_terminal?('c', false, index)) 298 | r12 = true 299 | @index += match_len 300 | else 301 | terminal_parse_failure('\'c\'') 302 | r12 = nil 303 | end 304 | if r12 305 | r12 = SyntaxNode.new(input, (index-1)...index) if r12 == true 306 | r1 = r12 307 | else 308 | if (match_len = has_terminal?('q', false, index)) 309 | r13 = true 310 | @index += match_len 311 | else 312 | terminal_parse_failure('\'q\'') 313 | r13 = nil 314 | end 315 | if r13 316 | r13 = SyntaxNode.new(input, (index-1)...index) if r13 == true 317 | r1 = r13 318 | else 319 | if (match_len = has_terminal?('a', false, index)) 320 | r14 = true 321 | @index += match_len 322 | else 323 | terminal_parse_failure('\'a\'') 324 | r14 = nil 325 | end 326 | if r14 327 | r14 = SyntaxNode.new(input, (index-1)...index) if r14 == true 328 | r1 = r14 329 | else 330 | if (match_len = has_terminal?('r', false, index)) 331 | r15 = true 332 | @index += match_len 333 | else 334 | terminal_parse_failure('\'r\'') 335 | r15 = nil 336 | end 337 | if r15 338 | r15 = SyntaxNode.new(input, (index-1)...index) if r15 == true 339 | r1 = r15 340 | else 341 | if (match_len = has_terminal?('m', false, index)) 342 | r16 = true 343 | @index += match_len 344 | else 345 | terminal_parse_failure('\'m\'') 346 | r16 = nil 347 | end 348 | if r16 349 | r16 = SyntaxNode.new(input, (index-1)...index) if r16 == true 350 | r1 = r16 351 | else 352 | if (match_len = has_terminal?('p', false, index)) 353 | r17 = true 354 | @index += match_len 355 | else 356 | terminal_parse_failure('\'p\'') 357 | r17 = nil 358 | end 359 | if r17 360 | r17 = SyntaxNode.new(input, (index-1)...index) if r17 == true 361 | r1 = r17 362 | else 363 | @index = i1 364 | r1 = nil 365 | end 366 | end 367 | end 368 | end 369 | end 370 | end 371 | end 372 | end 373 | end 374 | end 375 | end 376 | end 377 | end 378 | end 379 | end 380 | end 381 | if r1 382 | s0 << r1 383 | else 384 | break 385 | end 386 | end 387 | if s0.empty? 388 | @index = i0 389 | r0 = nil 390 | else 391 | r0 = instantiate_node(PassthroughInlineMacroSub,input, i0...index, s0) 392 | end 393 | 394 | node_cache[:passthrough_inline_macro_sub][start_index] = r0 395 | 396 | r0 397 | end 398 | 399 | def _nt_passthrough_inline_macro_content 400 | start_index = index 401 | if node_cache[:passthrough_inline_macro_content].has_key?(index) 402 | cached = node_cache[:passthrough_inline_macro_content][index] 403 | if cached 404 | node_cache[:passthrough_inline_macro_content][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 405 | @index = cached.interval.end 406 | end 407 | return cached 408 | end 409 | 410 | s0, i0 = [], index 411 | loop do 412 | i1 = index 413 | if (match_len = has_terminal?('\]', false, index)) 414 | r2 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 415 | @index += match_len 416 | else 417 | terminal_parse_failure('\'\\]\'') 418 | r2 = nil 419 | end 420 | if r2 421 | r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true 422 | r1 = r2 423 | else 424 | if has_terminal?(@regexps[gr = '\A[^\\]]'] ||= Regexp.new(gr), :regexp, index) 425 | r3 = true 426 | @index += 1 427 | else 428 | terminal_parse_failure('[^\\]]') 429 | r3 = nil 430 | end 431 | if r3 432 | r3 = SyntaxNode.new(input, (index-1)...index) if r3 == true 433 | r1 = r3 434 | else 435 | @index = i1 436 | r1 = nil 437 | end 438 | end 439 | if r1 440 | s0 << r1 441 | else 442 | break 443 | end 444 | end 445 | r0 = instantiate_node(PassthroughInlineMacroContent,input, i0...index, s0) 446 | 447 | node_cache[:passthrough_inline_macro_content][start_index] = r0 448 | 449 | r0 450 | end 451 | 452 | module PassthroughTriplePlus0 453 | def passthrough_triple_plus_content 454 | elements[1] 455 | end 456 | 457 | end 458 | 459 | def _nt_passthrough_triple_plus 460 | start_index = index 461 | if node_cache[:passthrough_triple_plus].has_key?(index) 462 | cached = node_cache[:passthrough_triple_plus][index] 463 | if cached 464 | node_cache[:passthrough_triple_plus][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 465 | @index = cached.interval.end 466 | end 467 | return cached 468 | end 469 | 470 | i0, s0 = index, [] 471 | if (match_len = has_terminal?('+++', false, index)) 472 | r1 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 473 | @index += match_len 474 | else 475 | terminal_parse_failure('\'+++\'') 476 | r1 = nil 477 | end 478 | s0 << r1 479 | if r1 480 | r2 = _nt_passthrough_triple_plus_content 481 | s0 << r2 482 | if r2 483 | if (match_len = has_terminal?('+++', false, index)) 484 | r3 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 485 | @index += match_len 486 | else 487 | terminal_parse_failure('\'+++\'') 488 | r3 = nil 489 | end 490 | s0 << r3 491 | end 492 | end 493 | if s0.last 494 | r0 = instantiate_node(PassthroughTriplePlus,input, i0...index, s0) 495 | r0.extend(PassthroughTriplePlus0) 496 | else 497 | @index = i0 498 | r0 = nil 499 | end 500 | 501 | node_cache[:passthrough_triple_plus][start_index] = r0 502 | 503 | r0 504 | end 505 | 506 | def _nt_passthrough_triple_plus_content 507 | start_index = index 508 | if node_cache[:passthrough_triple_plus_content].has_key?(index) 509 | cached = node_cache[:passthrough_triple_plus_content][index] 510 | if cached 511 | node_cache[:passthrough_triple_plus_content][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 512 | @index = cached.interval.end 513 | end 514 | return cached 515 | end 516 | 517 | s0, i0 = [], index 518 | loop do 519 | if has_terminal?(@regexps[gr = '\A[^+]'] ||= Regexp.new(gr), :regexp, index) 520 | r1 = true 521 | @index += 1 522 | else 523 | terminal_parse_failure('[^+]') 524 | r1 = nil 525 | end 526 | if r1 527 | s0 << r1 528 | else 529 | break 530 | end 531 | end 532 | if s0.empty? 533 | @index = i0 534 | r0 = nil 535 | else 536 | r0 = instantiate_node(PassthroughTriplePlusContent,input, i0...index, s0) 537 | end 538 | 539 | node_cache[:passthrough_triple_plus_content][start_index] = r0 540 | 541 | r0 542 | end 543 | 544 | end 545 | 546 | class AsciidoctorPassthroughGrammarParser < Treetop::Runtime::CompiledParser 547 | include AsciidoctorPassthroughGrammar 548 | end 549 | 550 | -------------------------------------------------------------------------------- /lib/asciidoctor/inline_parser/node_extensions.rb: -------------------------------------------------------------------------------- 1 | require_relative 'subs' 2 | 3 | module AsciidoctorGrammar 4 | # Text 5 | class Text < ::Treetop::Runtime::SyntaxNode 6 | def node_name 7 | 'text' 8 | end 9 | end 10 | 11 | class Sentence < ::Treetop::Runtime::SyntaxNode 12 | end 13 | 14 | # Expression 15 | class Expression < ::Treetop::Runtime::SyntaxNode 16 | def node_name 17 | 'inline' 18 | end 19 | end 20 | 21 | # Quoted content 22 | class QuotedContent < ::Treetop::Runtime::SyntaxNode 23 | def node_name 24 | 'formatted' 25 | end 26 | end 27 | 28 | # Quoted node 29 | class QuotedNode < ::Treetop::Runtime::SyntaxNode 30 | def content 31 | @elements.select { |el| el.instance_of? ::AsciidoctorGrammar::QuotedContent }.first.text_value 32 | end 33 | 34 | def nested? 35 | @elements.any? { |el| el.class.ancestors.include? ::AsciidoctorGrammar::QuotedNode } 36 | end 37 | 38 | def roles 39 | attributes_node = @elements.select { |el| el.instance_of? ::AsciidoctorGrammar::QuotedTextAttributes }.first 40 | attributes_node.roles if attributes_node 41 | end 42 | 43 | def id 44 | attributes_node = @elements.select { |el| el.instance_of? ::AsciidoctorGrammar::QuotedTextAttributes }.first 45 | attributes_node.id if attributes_node 46 | end 47 | end 48 | 49 | # Strong 50 | class StrongQuoted < ::AsciidoctorGrammar::QuotedNode 51 | def node_name 52 | 'strong' 53 | end 54 | end 55 | 56 | # Emphasis 57 | class EmphasisQuoted < ::AsciidoctorGrammar::QuotedNode 58 | def node_name 59 | 'emphasis' 60 | end 61 | end 62 | 63 | # Monospaced 64 | class MonospacedQuoted < ::AsciidoctorGrammar::QuotedNode 65 | def node_name 66 | 'monospaced' 67 | end 68 | end 69 | 70 | # Double curved 71 | class DoubleCurvedQuoted < ::AsciidoctorGrammar::QuotedNode 72 | def node_name 73 | 'double_quotation' 74 | end 75 | end 76 | 77 | # Single curved 78 | class SingleCurvedQuoted < ::AsciidoctorGrammar::QuotedNode 79 | def node_name 80 | 'single_quotation' 81 | end 82 | end 83 | 84 | # Literal 85 | class Literal < ::Treetop::Runtime::SyntaxNode 86 | def node_name 87 | 'literal' 88 | end 89 | end 90 | 91 | # Literal (single) line 92 | class LiteralLine < ::Treetop::Runtime::SyntaxNode 93 | def node_name 94 | 'literal_line' 95 | end 96 | 97 | def text 98 | input 99 | end 100 | end 101 | 102 | # Mark 103 | class MarkQuoted < ::AsciidoctorGrammar::QuotedNode 104 | end 105 | 106 | # Quoted text anchor (id) 107 | class QuotedTextRole < ::AsciidoctorGrammar::QuotedNode 108 | def name 109 | role_node = @comprehensive_elements.select { |el| el.instance_of? ::AsciidoctorGrammar::RoleIdentifier }.first 110 | role_node.text_value if role_node 111 | end 112 | end 113 | 114 | # Quoted text role 115 | class QuotedTextAnchor < ::AsciidoctorGrammar::QuotedNode 116 | def name 117 | anchor_node = @comprehensive_elements.select { |el| el.instance_of? ::AsciidoctorGrammar::AnchorIdentifier }.first 118 | anchor_node.text_value if anchor_node 119 | end 120 | end 121 | 122 | # Quoted text attributes: anchor and roles 123 | class QuotedTextAttributes < ::Treetop::Runtime::SyntaxNode 124 | def roles 125 | content_node = @comprehensive_elements.select do |el| 126 | el.instance_of? ::AsciidoctorGrammar::QuotedTextAttributesContent 127 | end.first 128 | content_node.roles if content_node 129 | end 130 | 131 | def id 132 | content_node = @comprehensive_elements.select do |el| 133 | el.instance_of? ::AsciidoctorGrammar::QuotedTextAttributesContent 134 | end.first 135 | content_node.id if content_node 136 | end 137 | end 138 | 139 | # Quoted text attributes content: anchor and roles 140 | class QuotedTextAttributesContent < ::Treetop::Runtime::SyntaxNode 141 | def roles 142 | @comprehensive_elements.select { |el| el.instance_of? ::AsciidoctorGrammar::QuotedTextRole }.map(&:name).compact 143 | end 144 | 145 | def id 146 | anchor_node = @comprehensive_elements.select { |el| el.instance_of? ::AsciidoctorGrammar::QuotedTextAnchor }.first 147 | anchor_node.name if anchor_node 148 | end 149 | end 150 | 151 | class MacroAttributes < ::Treetop::Runtime::SyntaxNode 152 | end 153 | 154 | class RoleIdentifier < ::Treetop::Runtime::SyntaxNode 155 | end 156 | 157 | class AnchorIdentifier < ::Treetop::Runtime::SyntaxNode 158 | end 159 | 160 | class SuperscriptQuoted < ::AsciidoctorGrammar::QuotedNode 161 | end 162 | 163 | class SubscriptQuoted < ::AsciidoctorGrammar::QuotedNode 164 | end 165 | end 166 | 167 | module AsciidoctorLinkGrammar 168 | # Link 169 | class Link < ::Treetop::Runtime::SyntaxNode 170 | BLANK_SHORTHAND = '^'.freeze 171 | 172 | def node_name 173 | 'anchor' 174 | end 175 | 176 | def target 177 | scheme + path 178 | end 179 | 180 | def scheme 181 | scheme_node = @comprehensive_elements.select { |el| el.instance_of? ::AsciidoctorGrammar::LinkScheme }.first 182 | scheme_node.text_value if scheme_node 183 | end 184 | 185 | def path 186 | path_node = @comprehensive_elements.select { |el| el.instance_of? ::AsciidoctorGrammar::LinkPath }.first 187 | path_node.text_value if path_node 188 | end 189 | 190 | def text 191 | attrs_node = @comprehensive_elements.select { |el| el.instance_of? ::AsciidoctorGrammar::LinkAttributes }.first 192 | if attrs_node 193 | text = attrs_node.text 194 | return text.chop if text.end_with? BLANK_SHORTHAND 195 | return text 196 | end 197 | target 198 | end 199 | 200 | def window 201 | attrs_node = @comprehensive_elements.select { |el| el.instance_of? ::AsciidoctorGrammar::LinkAttributes }.first 202 | return '_blank' if attrs_node && (attrs_node.text.end_with? BLANK_SHORTHAND) 203 | attrs_node.window if attrs_node && attrs_node.window 204 | end 205 | 206 | def roles 207 | attrs_node = @comprehensive_elements.select { |el| el.instance_of? ::AsciidoctorGrammar::LinkAttributes }.first 208 | return ['bare'] if attrs_node.nil? || (attrs_node.roles.empty? && attrs_node.text.nil?) 209 | attrs_node.roles 210 | end 211 | end 212 | 213 | # Link scheme 214 | class LinkScheme < ::Treetop::Runtime::SyntaxNode 215 | def node_name 216 | 'link_scheme' 217 | end 218 | end 219 | 220 | # Link path 221 | class LinkPath < ::Treetop::Runtime::SyntaxNode 222 | def node_name 223 | 'link_path' 224 | end 225 | end 226 | 227 | # Link attributes (content) 228 | class LinkAttributesContent < ::Treetop::Runtime::SyntaxNode 229 | def roles 230 | @comprehensive_elements.select { |el| el.instance_of? ::AsciidoctorGrammar::LinkRole }.map(&:name).compact 231 | end 232 | 233 | def text 234 | text_node = @comprehensive_elements.select { |el| el.instance_of? ::AsciidoctorGrammar::LinkText }.first 235 | return text_node.name if text_node 236 | protected_text_node = @comprehensive_elements.select do |el| 237 | el.instance_of? ::AsciidoctorGrammar::LinkTextProtected 238 | end.first 239 | protected_text_node.name if protected_text_node 240 | end 241 | 242 | def window 243 | window_node = @comprehensive_elements.select { |el| el.instance_of? ::AsciidoctorGrammar::LinkWindow }.first 244 | window_node.name if window_node 245 | end 246 | end 247 | 248 | # Link attributes 249 | class LinkAttributes < ::Treetop::Runtime::SyntaxNode 250 | def text 251 | attrs_node = @comprehensive_elements.select { |el| link_attributes_content? el }.first 252 | attrs_node.text if attrs_node 253 | end 254 | 255 | def roles 256 | attrs_node = @comprehensive_elements.select { |el| link_attributes_content? el }.first 257 | attrs_node.roles if attrs_node 258 | end 259 | 260 | def window 261 | attrs_node = @comprehensive_elements.select { |el| link_attributes_content? el }.first 262 | attrs_node.window if attrs_node 263 | end 264 | 265 | private 266 | 267 | def link_attributes_content? node 268 | node.instance_of? ::AsciidoctorGrammar::LinkAttributesContent 269 | end 270 | end 271 | 272 | # Link role 273 | class LinkRole < ::Treetop::Runtime::SyntaxNode 274 | def name 275 | @elements[1].text_value # [0]: role=" [1]: name 276 | end 277 | end 278 | 279 | # Link window 280 | class LinkWindow < ::Treetop::Runtime::SyntaxNode 281 | def name 282 | @elements[1].text_value # [0]: window=" [1]: name 283 | end 284 | end 285 | 286 | # Link text 287 | class LinkText < ::Treetop::Runtime::SyntaxNode 288 | def name 289 | text_value 290 | end 291 | end 292 | 293 | # Link text protected with double quotes 294 | class LinkTextProtected < ::Treetop::Runtime::SyntaxNode 295 | def name 296 | @elements[1].text_value # [0]: " [1]: name 297 | end 298 | end 299 | end 300 | 301 | module AsciidoctorEmailGrammar 302 | # Email macro 303 | class EmailMacro < ::Treetop::Runtime::SyntaxNode 304 | def node_name 305 | 'email_macro' 306 | end 307 | 308 | def name 309 | attrs_node = @comprehensive_elements.select { |el| email_attrs? el }.first 310 | return attrs_node.name if attrs_node && attrs_node.name 311 | email 312 | end 313 | 314 | def subject 315 | attrs_node = @comprehensive_elements.select { |el| email_attrs? el }.first 316 | attrs_node.subject if attrs_node 317 | end 318 | 319 | def body 320 | attrs_node = @comprehensive_elements.select { |el| email_attrs? el }.first 321 | attrs_node.body if attrs_node 322 | end 323 | 324 | def email 325 | email_node = @comprehensive_elements.select { |el| email? el }.first 326 | email_node.email if email_node 327 | end 328 | 329 | def email? node 330 | node.instance_of? ::AsciidoctorEmailGrammar::Email 331 | end 332 | 333 | def email_attrs? node 334 | node.instance_of? ::AsciidoctorEmailGrammar::EmailAttributes 335 | end 336 | end 337 | # Email 338 | class Email < ::Treetop::Runtime::SyntaxNode 339 | def node_name 340 | 'email' 341 | end 342 | 343 | def text 344 | email 345 | end 346 | 347 | def email 348 | text_value 349 | end 350 | end 351 | # Email attributes 352 | class EmailAttributes < ::Treetop::Runtime::SyntaxNode 353 | def name 354 | attrs_node = @comprehensive_elements.select { |el| email_attrs_content? el }.first 355 | attrs_node.name if attrs_node 356 | end 357 | 358 | def subject 359 | attrs_node = @comprehensive_elements.select { |el| email_attrs_content? el }.first 360 | attrs_node.subject if attrs_node 361 | end 362 | 363 | def body 364 | attrs_node = @comprehensive_elements.select { |el| email_attrs_content? el }.first 365 | attrs_node.body if attrs_node 366 | end 367 | 368 | private 369 | 370 | def email_attrs_content? node 371 | node.instance_of? ::AsciidoctorEmailGrammar::EmailAttributesContent 372 | end 373 | end 374 | # Email attributes content 375 | class EmailAttributesContent < ::Treetop::Runtime::SyntaxNode 376 | def name 377 | attrs.first.text_value if attrs && !attrs.empty? 378 | end 379 | 380 | def subject 381 | attrs[1].text_value if attrs && attrs.length > 1 382 | end 383 | 384 | def body 385 | attrs[2].text_value if attrs && attrs.length > 2 386 | end 387 | 388 | def attrs 389 | @comprehensive_elements.select { |el| email_text? el } 390 | end 391 | 392 | private 393 | 394 | def email_text? node 395 | node.instance_of? ::AsciidoctorEmailGrammar::EmailText 396 | end 397 | end 398 | class EmailTextProtected < ::Treetop::Runtime::SyntaxNode 399 | end 400 | class EmailText < ::Treetop::Runtime::SyntaxNode 401 | end 402 | # Escaped email 403 | class EscapedEmail < ::Treetop::Runtime::SyntaxNode 404 | def node_name 405 | 'escaped_email' 406 | end 407 | end 408 | end 409 | 410 | module AsciidoctorPassthroughGrammar 411 | # Escaped passthrough inline macro 412 | class EscapedPassthroughInlineMacro < ::Treetop::Runtime::SyntaxNode 413 | def node_name 414 | 'passthrough' 415 | end 416 | end 417 | 418 | # Passthrough inline macro 419 | class PassthroughInlineMacro < ::Treetop::Runtime::SyntaxNode 420 | def node_name 421 | 'passthrough_inline' 422 | end 423 | 424 | def content 425 | content_node = @comprehensive_elements.select { |el| passthrough_inline_macro_content? el }.first 426 | content_node.text_value if content_node 427 | end 428 | 429 | def subs 430 | subs_node = @comprehensive_elements.select { |el| passthrough_inline_macro_subs? el }.first 431 | subs_node.subs if subs_node 432 | end 433 | 434 | def apply_subs text 435 | # TODO: Implement all the substitutions 436 | text = text.gsub '\]', ']' 437 | if (subs.include? 'verbatim') || (subs.include? 'specialcharacters') || (subs.include? 'specialchars') 438 | text = ::Asciidoctor::Subs.sub_specialchars text 439 | end 440 | text 441 | end 442 | 443 | def passthrough_inline_macro_content? node 444 | node.instance_of? ::AsciidoctorGrammar::PassthroughInlineMacroContent 445 | end 446 | 447 | def passthrough_inline_macro_subs? node 448 | node.instance_of? ::AsciidoctorGrammar::PassthroughInlineMacroSubs 449 | end 450 | end 451 | # Passthrough inline macro subs 452 | class PassthroughInlineMacroSubs < ::Treetop::Runtime::SyntaxNode 453 | def subs 454 | @comprehensive_elements 455 | .select { |el| el.instance_of? ::AsciidoctorGrammar::PassthroughInlineMacroSub } 456 | .map(&:text_value) 457 | end 458 | end 459 | class PassthroughInlineMacroSub < ::Treetop::Runtime::SyntaxNode 460 | end 461 | class PassthroughInlineMacroContent < ::Treetop::Runtime::SyntaxNode 462 | end 463 | # Passthrough using +++ (triple plus) 464 | class PassthroughTriplePlus < ::Treetop::Runtime::SyntaxNode 465 | def node_name 466 | 'passthrough_triple_plus' 467 | end 468 | 469 | def content 470 | content_node = @comprehensive_elements.select { |el| passthrough_triple_plus_content? el }.first 471 | content_node.text_value if content_node 472 | end 473 | 474 | private 475 | 476 | def passthrough_triple_plus_content? node 477 | node.instance_of? ::AsciidoctorGrammar::PassthroughTriplePlusContent 478 | end 479 | end 480 | class PassthroughTriplePlusContent < ::Treetop::Runtime::SyntaxNode 481 | end 482 | end 483 | 484 | module AsciidoctorImageGrammar 485 | # Image 486 | class Image < ::Treetop::Runtime::SyntaxNode 487 | def node_name 488 | 'image' 489 | end 490 | 491 | def roles 492 | get_attr 'roles' 493 | end 494 | 495 | def target 496 | target_node = @comprehensive_elements.select { |el| image_target? el }.first 497 | target_node.name if target_node 498 | end 499 | 500 | def alt 501 | value = get_attr 'alt' 502 | if value 503 | value 504 | else 505 | extname = File.extname(target) 506 | ::File.basename(target, extname) 507 | end 508 | end 509 | 510 | def title 511 | get_attr 'title' 512 | end 513 | 514 | def width 515 | get_attr 'width' 516 | end 517 | 518 | def height 519 | get_attr 'height' 520 | end 521 | 522 | private 523 | 524 | def get_attr name 525 | attrs_node = @comprehensive_elements.select { |el| image_attrs? el }.first 526 | attrs_node.public_send(name) if attrs_node && (attrs_node.respond_to? name) 527 | end 528 | 529 | def image_target? node 530 | node.instance_of? ::AsciidoctorImageGrammar::ImageTarget 531 | end 532 | 533 | def image_attrs? node 534 | node.instance_of? ::AsciidoctorImageGrammar::ImageAttributes 535 | end 536 | end 537 | # Attributes 538 | class ImageAttributes < ::Treetop::Runtime::SyntaxNode 539 | def title 540 | content_node = @comprehensive_elements.select { |el| attr_content? el }.first 541 | # named attribute 'title' 542 | content_node.attrs['title'] if content_node 543 | end 544 | 545 | def alt 546 | content_node = @comprehensive_elements.select { |el| attr_content? el }.first 547 | # named attribute 'alt' or the first attribute (0 based index) 548 | content_node.attrs['alt'] || content_node.attrs['0'] if content_node 549 | end 550 | 551 | def width 552 | content_node = @comprehensive_elements.select { |el| attr_content? el }.first 553 | # named attribute 'alt' or the second attribute (0 based index) 554 | content_node.attrs['width'] || content_node.attrs['1'] if content_node 555 | end 556 | 557 | def height 558 | content_node = @comprehensive_elements.select { |el| attr_content? el }.first 559 | # named attribute 'alt' or the third attribute (0 based index) 560 | content_node.attrs['height'] || content_node.attrs['2'] if content_node 561 | end 562 | 563 | def roles 564 | result = ['image'] 565 | content_node = @comprehensive_elements.select { |el| attr_content? el }.first 566 | # named attribute 'alt' or the fourth attribute (0 based index) 567 | if content_node 568 | roles = content_node.attrs['role'] || content_node.attrs['3'] || '' 569 | result += roles.split(',').map { |role| role.strip! || role } 570 | end 571 | result 572 | end 573 | 574 | private 575 | 576 | def attr_content? node 577 | node.instance_of? ::AsciidoctorImageGrammar::ImageAttributesContent 578 | end 579 | end 580 | # Image target 581 | class ImageTarget < ::Treetop::Runtime::SyntaxNode 582 | def name 583 | text_value 584 | end 585 | end 586 | # Attributes content 587 | class ImageAttributesContent < ::Treetop::Runtime::SyntaxNode 588 | def attrs 589 | @comprehensive_elements.each_with_index.map do |el, index| 590 | if el.instance_of? ::AsciidoctorImageGrammar::ImageNamedAttribute 591 | { el.key => el.value } 592 | else 593 | { index.to_s => el.value } 594 | end 595 | end.inject(:merge) 596 | end 597 | end 598 | # Named attribute 599 | class ImageNamedAttribute < ::Treetop::Runtime::SyntaxNode 600 | def key 601 | key_node = @comprehensive_elements.select { |el| attr_key? el }.first 602 | key_node.text_value if key_node 603 | end 604 | 605 | def value 606 | value_node = @comprehensive_elements.select { |el| attr_value? el }.first 607 | value_node.value if value_node 608 | end 609 | 610 | private 611 | 612 | def attr_key? node 613 | node.instance_of? ::AsciidoctorImageGrammar::ImageNamedAttributeKey 614 | end 615 | 616 | def attr_value? node 617 | (attr_value_unprotected? node) || (attr_value_protected? node) 618 | end 619 | 620 | def attr_value_unprotected? node 621 | node.instance_of? ::AsciidoctorImageGrammar::ImageNamedAttributeValue 622 | end 623 | 624 | def attr_value_protected? node 625 | node.instance_of? ::AsciidoctorImageGrammar::ImageAttributeValueProtected 626 | end 627 | end 628 | class ImageNamedAttributeKey < ::Treetop::Runtime::SyntaxNode 629 | end 630 | # Named attribute value 631 | class ImageNamedAttributeValue < ::Treetop::Runtime::SyntaxNode 632 | def value 633 | @comprehensive_elements.first.value 634 | end 635 | end 636 | # Attribute value protected with double quotes 637 | class ImageAttributeValueProtected < ::Treetop::Runtime::SyntaxNode 638 | def value 639 | @elements[1].text_value # [0]: " [1]: name 640 | end 641 | end 642 | # Attribute value 643 | class ImageAttributeValue < ::Treetop::Runtime::SyntaxNode 644 | def value 645 | text_value 646 | end 647 | end 648 | end 649 | 650 | module AsciidoctorKbdGrammar 651 | # Keybord kbd inline macro 652 | class Kbd < ::Treetop::Runtime::SyntaxNode 653 | def node_name 654 | 'kbd' 655 | end 656 | 657 | def keys 658 | content_node = @comprehensive_elements.select { |el| attr_content? el }.first 659 | content_node.text_value.split('+').map(&:strip) if content_node 660 | end 661 | 662 | private 663 | 664 | def attr_content? node 665 | node.instance_of? ::AsciidoctorKbdGrammar::KbdContent 666 | end 667 | end 668 | class KbdContent < ::Treetop::Runtime::SyntaxNode 669 | end 670 | end 671 | 672 | module AsciidoctorBtnGrammar 673 | # Button btn inline macro 674 | class Btn < ::Treetop::Runtime::SyntaxNode 675 | def node_name 676 | 'btn' 677 | end 678 | 679 | def text 680 | content_node = @comprehensive_elements.select { |el| attr_content? el }.first 681 | content_node.text_value if content_node 682 | end 683 | 684 | private 685 | 686 | def attr_content? node 687 | node.instance_of? ::AsciidoctorBtnGrammar::BtnContent 688 | end 689 | end 690 | class BtnContent < ::Treetop::Runtime::SyntaxNode 691 | end 692 | end 693 | 694 | module AsciidoctorMenuGrammar 695 | # Menu inline macro 696 | class Menu < ::Treetop::Runtime::SyntaxNode 697 | def node_name 698 | 'menu' 699 | end 700 | 701 | def items 702 | result = [] 703 | target_node = @comprehensive_elements.select { |el| target? el }.first 704 | result.push(target_node.text_value) if target_node 705 | content_node = @comprehensive_elements.select { |el| content? el }.first 706 | result.concat(content_node.text_value.split('>').map(&:strip)) if content_node 707 | result 708 | end 709 | 710 | private 711 | 712 | def content? node 713 | node.instance_of? ::AsciidoctorMenuGrammar::MenuContent 714 | end 715 | 716 | def target? node 717 | node.instance_of? ::AsciidoctorMenuGrammar::MenuTarget 718 | end 719 | end 720 | class MenuTarget < ::Treetop::Runtime::SyntaxNode 721 | end 722 | class MenuContent < ::Treetop::Runtime::SyntaxNode 723 | end 724 | end 725 | -------------------------------------------------------------------------------- /lib/asciidoctor/inline_parser/asciidoctor_link_grammar.rb: -------------------------------------------------------------------------------- 1 | # Autogenerated from a Treetop grammar. Edits may be lost. 2 | 3 | 4 | module AsciidoctorLinkGrammar 5 | include Treetop::Runtime 6 | 7 | def root 8 | @root ||= :explicit_link 9 | end 10 | 11 | module ExplicitLink0 12 | def uri_scheme 13 | elements[1] 14 | end 15 | 16 | def uri_path_explicit 17 | elements[2] 18 | end 19 | 20 | end 21 | 22 | def _nt_explicit_link 23 | start_index = index 24 | if node_cache[:explicit_link].has_key?(index) 25 | cached = node_cache[:explicit_link][index] 26 | if cached 27 | node_cache[:explicit_link][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 28 | @index = cached.interval.end 29 | end 30 | return cached 31 | end 32 | 33 | i0, s0 = index, [] 34 | if (match_len = has_terminal?('link:', false, index)) 35 | r1 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 36 | @index += match_len 37 | else 38 | terminal_parse_failure('\'link:\'') 39 | r1 = nil 40 | end 41 | s0 << r1 42 | if r1 43 | r2 = _nt_uri_scheme 44 | s0 << r2 45 | if r2 46 | r3 = _nt_uri_path_explicit 47 | s0 << r3 48 | if r3 49 | r5 = _nt_link_attrs 50 | if r5 51 | r4 = r5 52 | else 53 | r4 = instantiate_node(SyntaxNode,input, index...index) 54 | end 55 | s0 << r4 56 | end 57 | end 58 | end 59 | if s0.last 60 | r0 = instantiate_node(Link,input, i0...index, s0) 61 | r0.extend(ExplicitLink0) 62 | else 63 | @index = i0 64 | r0 = nil 65 | end 66 | 67 | node_cache[:explicit_link][start_index] = r0 68 | 69 | r0 70 | end 71 | 72 | module ExplicitLinkProtected0 73 | def uri_scheme 74 | elements[1] 75 | end 76 | 77 | def uri_path_protected 78 | elements[2] 79 | end 80 | 81 | end 82 | 83 | def _nt_explicit_link_protected 84 | start_index = index 85 | if node_cache[:explicit_link_protected].has_key?(index) 86 | cached = node_cache[:explicit_link_protected][index] 87 | if cached 88 | node_cache[:explicit_link_protected][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 89 | @index = cached.interval.end 90 | end 91 | return cached 92 | end 93 | 94 | i0, s0 = index, [] 95 | if (match_len = has_terminal?('link:++', false, index)) 96 | r1 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 97 | @index += match_len 98 | else 99 | terminal_parse_failure('\'link:++\'') 100 | r1 = nil 101 | end 102 | s0 << r1 103 | if r1 104 | r2 = _nt_uri_scheme 105 | s0 << r2 106 | if r2 107 | r3 = _nt_uri_path_protected 108 | s0 << r3 109 | if r3 110 | if (match_len = has_terminal?('++', false, index)) 111 | r4 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 112 | @index += match_len 113 | else 114 | terminal_parse_failure('\'++\'') 115 | r4 = nil 116 | end 117 | s0 << r4 118 | if r4 119 | r6 = _nt_link_attrs 120 | if r6 121 | r5 = r6 122 | else 123 | r5 = instantiate_node(SyntaxNode,input, index...index) 124 | end 125 | s0 << r5 126 | end 127 | end 128 | end 129 | end 130 | if s0.last 131 | r0 = instantiate_node(Link,input, i0...index, s0) 132 | r0.extend(ExplicitLinkProtected0) 133 | else 134 | @index = i0 135 | r0 = nil 136 | end 137 | 138 | node_cache[:explicit_link_protected][start_index] = r0 139 | 140 | r0 141 | end 142 | 143 | def _nt_uri_path_explicit 144 | start_index = index 145 | if node_cache[:uri_path_explicit].has_key?(index) 146 | cached = node_cache[:uri_path_explicit][index] 147 | if cached 148 | node_cache[:uri_path_explicit][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 149 | @index = cached.interval.end 150 | end 151 | return cached 152 | end 153 | 154 | s0, i0 = [], index 155 | loop do 156 | if has_terminal?(@regexps[gr = '\A[^\\[]'] ||= Regexp.new(gr), :regexp, index) 157 | r1 = true 158 | @index += 1 159 | else 160 | terminal_parse_failure('[^\\[]') 161 | r1 = nil 162 | end 163 | if r1 164 | s0 << r1 165 | else 166 | break 167 | end 168 | end 169 | if s0.empty? 170 | @index = i0 171 | r0 = nil 172 | else 173 | r0 = instantiate_node(LinkPath,input, i0...index, s0) 174 | end 175 | 176 | node_cache[:uri_path_explicit][start_index] = r0 177 | 178 | r0 179 | end 180 | 181 | def _nt_uri_path_protected 182 | start_index = index 183 | if node_cache[:uri_path_protected].has_key?(index) 184 | cached = node_cache[:uri_path_protected][index] 185 | if cached 186 | node_cache[:uri_path_protected][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 187 | @index = cached.interval.end 188 | end 189 | return cached 190 | end 191 | 192 | s0, i0 = [], index 193 | loop do 194 | if has_terminal?(@regexps[gr = '\A[^+]'] ||= Regexp.new(gr), :regexp, index) 195 | r1 = true 196 | @index += 1 197 | else 198 | terminal_parse_failure('[^+]') 199 | r1 = nil 200 | end 201 | if r1 202 | s0 << r1 203 | else 204 | break 205 | end 206 | end 207 | if s0.empty? 208 | @index = i0 209 | r0 = nil 210 | else 211 | r0 = instantiate_node(LinkPath,input, i0...index, s0) 212 | end 213 | 214 | node_cache[:uri_path_protected][start_index] = r0 215 | 216 | r0 217 | end 218 | 219 | module ImplicitLink0 220 | def uri_scheme 221 | elements[0] 222 | end 223 | 224 | def uri_path 225 | elements[1] 226 | end 227 | 228 | end 229 | 230 | def _nt_implicit_link 231 | start_index = index 232 | if node_cache[:implicit_link].has_key?(index) 233 | cached = node_cache[:implicit_link][index] 234 | if cached 235 | node_cache[:implicit_link][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 236 | @index = cached.interval.end 237 | end 238 | return cached 239 | end 240 | 241 | i0, s0 = index, [] 242 | r1 = _nt_uri_scheme 243 | s0 << r1 244 | if r1 245 | r2 = _nt_uri_path 246 | s0 << r2 247 | if r2 248 | r4 = _nt_link_attrs 249 | if r4 250 | r3 = r4 251 | else 252 | r3 = instantiate_node(SyntaxNode,input, index...index) 253 | end 254 | s0 << r3 255 | end 256 | end 257 | if s0.last 258 | r0 = instantiate_node(Link,input, i0...index, s0) 259 | r0.extend(ImplicitLink0) 260 | else 261 | @index = i0 262 | r0 = nil 263 | end 264 | 265 | node_cache[:implicit_link][start_index] = r0 266 | 267 | r0 268 | end 269 | 270 | module UriScheme0 271 | end 272 | 273 | def _nt_uri_scheme 274 | start_index = index 275 | if node_cache[:uri_scheme].has_key?(index) 276 | cached = node_cache[:uri_scheme][index] 277 | if cached 278 | node_cache[:uri_scheme][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 279 | @index = cached.interval.end 280 | end 281 | return cached 282 | end 283 | 284 | i0, s0 = index, [] 285 | i1 = index 286 | if (match_len = has_terminal?('https', false, index)) 287 | r2 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 288 | @index += match_len 289 | else 290 | terminal_parse_failure('\'https\'') 291 | r2 = nil 292 | end 293 | if r2 294 | r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true 295 | r1 = r2 296 | else 297 | if (match_len = has_terminal?('http', false, index)) 298 | r3 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 299 | @index += match_len 300 | else 301 | terminal_parse_failure('\'http\'') 302 | r3 = nil 303 | end 304 | if r3 305 | r3 = SyntaxNode.new(input, (index-1)...index) if r3 == true 306 | r1 = r3 307 | else 308 | if (match_len = has_terminal?('file', false, index)) 309 | r4 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 310 | @index += match_len 311 | else 312 | terminal_parse_failure('\'file\'') 313 | r4 = nil 314 | end 315 | if r4 316 | r4 = SyntaxNode.new(input, (index-1)...index) if r4 == true 317 | r1 = r4 318 | else 319 | if (match_len = has_terminal?('ftp', false, index)) 320 | r5 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 321 | @index += match_len 322 | else 323 | terminal_parse_failure('\'ftp\'') 324 | r5 = nil 325 | end 326 | if r5 327 | r5 = SyntaxNode.new(input, (index-1)...index) if r5 == true 328 | r1 = r5 329 | else 330 | if (match_len = has_terminal?('irc', false, index)) 331 | r6 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 332 | @index += match_len 333 | else 334 | terminal_parse_failure('\'irc\'') 335 | r6 = nil 336 | end 337 | if r6 338 | r6 = SyntaxNode.new(input, (index-1)...index) if r6 == true 339 | r1 = r6 340 | else 341 | @index = i1 342 | r1 = nil 343 | end 344 | end 345 | end 346 | end 347 | end 348 | s0 << r1 349 | if r1 350 | if (match_len = has_terminal?('://', false, index)) 351 | r7 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 352 | @index += match_len 353 | else 354 | terminal_parse_failure('\'://\'') 355 | r7 = nil 356 | end 357 | s0 << r7 358 | end 359 | if s0.last 360 | r0 = instantiate_node(LinkScheme,input, i0...index, s0) 361 | r0.extend(UriScheme0) 362 | else 363 | @index = i0 364 | r0 = nil 365 | end 366 | 367 | node_cache[:uri_scheme][start_index] = r0 368 | 369 | r0 370 | end 371 | 372 | module UriPath0 373 | end 374 | 375 | module UriPath1 376 | end 377 | 378 | def _nt_uri_path 379 | start_index = index 380 | if node_cache[:uri_path].has_key?(index) 381 | cached = node_cache[:uri_path][index] 382 | if cached 383 | node_cache[:uri_path][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 384 | @index = cached.interval.end 385 | end 386 | return cached 387 | end 388 | 389 | s0, i0 = [], index 390 | loop do 391 | i1 = index 392 | i2, s2 = index, [] 393 | if (match_len = has_terminal?('.', false, index)) 394 | r3 = true 395 | @index += match_len 396 | else 397 | terminal_parse_failure('\'.\'') 398 | r3 = nil 399 | end 400 | s2 << r3 401 | if r3 402 | i4, s4 = index, [] 403 | i5 = index 404 | if (match_len = has_terminal?(' ', false, index)) 405 | r6 = true 406 | @index += match_len 407 | else 408 | terminal_parse_failure('\' \'') 409 | r6 = nil 410 | end 411 | if r6 412 | @index = i5 413 | r5 = nil 414 | terminal_parse_failure('\' \'', true) 415 | else 416 | @terminal_failures.pop 417 | @index = i5 418 | r5 = instantiate_node(SyntaxNode,input, index...index) 419 | end 420 | s4 << r5 421 | if r5 422 | if index < input_length 423 | r7 = true 424 | @index += 1 425 | else 426 | terminal_parse_failure("any character") 427 | r7 = nil 428 | end 429 | s4 << r7 430 | end 431 | if s4.last 432 | r4 = instantiate_node(SyntaxNode,input, i4...index, s4) 433 | r4.extend(UriPath0) 434 | else 435 | @index = i4 436 | r4 = nil 437 | end 438 | s2 << r4 439 | end 440 | if s2.last 441 | r2 = instantiate_node(SyntaxNode,input, i2...index, s2) 442 | r2.extend(UriPath1) 443 | else 444 | @index = i2 445 | r2 = nil 446 | end 447 | if r2 448 | r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true 449 | r1 = r2 450 | else 451 | if has_terminal?(@regexps[gr = '\A[^ \\.\\[]'] ||= Regexp.new(gr), :regexp, index) 452 | r8 = true 453 | @index += 1 454 | else 455 | terminal_parse_failure('[^ \\.\\[]') 456 | r8 = nil 457 | end 458 | if r8 459 | r8 = SyntaxNode.new(input, (index-1)...index) if r8 == true 460 | r1 = r8 461 | else 462 | @index = i1 463 | r1 = nil 464 | end 465 | end 466 | if r1 467 | s0 << r1 468 | else 469 | break 470 | end 471 | end 472 | if s0.empty? 473 | @index = i0 474 | r0 = nil 475 | else 476 | r0 = instantiate_node(LinkPath,input, i0...index, s0) 477 | end 478 | 479 | node_cache[:uri_path][start_index] = r0 480 | 481 | r0 482 | end 483 | 484 | module LinkAttrs0 485 | def link_attrs_content 486 | elements[1] 487 | end 488 | 489 | end 490 | 491 | def _nt_link_attrs 492 | start_index = index 493 | if node_cache[:link_attrs].has_key?(index) 494 | cached = node_cache[:link_attrs][index] 495 | if cached 496 | node_cache[:link_attrs][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 497 | @index = cached.interval.end 498 | end 499 | return cached 500 | end 501 | 502 | i0, s0 = index, [] 503 | if (match_len = has_terminal?('[', false, index)) 504 | r1 = true 505 | @index += match_len 506 | else 507 | terminal_parse_failure('\'[\'') 508 | r1 = nil 509 | end 510 | s0 << r1 511 | if r1 512 | r2 = _nt_link_attrs_content 513 | s0 << r2 514 | if r2 515 | if (match_len = has_terminal?(']', false, index)) 516 | r3 = true 517 | @index += match_len 518 | else 519 | terminal_parse_failure('\']\'') 520 | r3 = nil 521 | end 522 | s0 << r3 523 | end 524 | end 525 | if s0.last 526 | r0 = instantiate_node(LinkAttributes,input, i0...index, s0) 527 | r0.extend(LinkAttrs0) 528 | else 529 | @index = i0 530 | r0 = nil 531 | end 532 | 533 | node_cache[:link_attrs][start_index] = r0 534 | 535 | r0 536 | end 537 | 538 | def _nt_link_attrs_content 539 | start_index = index 540 | if node_cache[:link_attrs_content].has_key?(index) 541 | cached = node_cache[:link_attrs_content][index] 542 | if cached 543 | node_cache[:link_attrs_content][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 544 | @index = cached.interval.end 545 | end 546 | return cached 547 | end 548 | 549 | s0, i0 = [], index 550 | loop do 551 | i1 = index 552 | r2 = _nt_link_attr_role_protected 553 | if r2 554 | r2 = SyntaxNode.new(input, (index-1)...index) if r2 == true 555 | r1 = r2 556 | else 557 | r3 = _nt_link_attr_role_unprotected 558 | if r3 559 | r3 = SyntaxNode.new(input, (index-1)...index) if r3 == true 560 | r1 = r3 561 | else 562 | r4 = _nt_link_attr_window_protected 563 | if r4 564 | r4 = SyntaxNode.new(input, (index-1)...index) if r4 == true 565 | r1 = r4 566 | else 567 | r5 = _nt_link_attr_text_protected 568 | if r5 569 | r5 = SyntaxNode.new(input, (index-1)...index) if r5 == true 570 | r1 = r5 571 | else 572 | if (match_len = has_terminal?(',', false, index)) 573 | r6 = true 574 | @index += match_len 575 | else 576 | terminal_parse_failure('\',\'') 577 | r6 = nil 578 | end 579 | if r6 580 | r6 = SyntaxNode.new(input, (index-1)...index) if r6 == true 581 | r1 = r6 582 | else 583 | if (match_len = has_terminal?(' ', false, index)) 584 | r7 = true 585 | @index += match_len 586 | else 587 | terminal_parse_failure('\' \'') 588 | r7 = nil 589 | end 590 | if r7 591 | r7 = SyntaxNode.new(input, (index-1)...index) if r7 == true 592 | r1 = r7 593 | else 594 | r8 = _nt_link_attr_text_unprotected 595 | if r8 596 | r8 = SyntaxNode.new(input, (index-1)...index) if r8 == true 597 | r1 = r8 598 | else 599 | r9 = _nt_link_attr_window_unprotected 600 | if r9 601 | r9 = SyntaxNode.new(input, (index-1)...index) if r9 == true 602 | r1 = r9 603 | else 604 | @index = i1 605 | r1 = nil 606 | end 607 | end 608 | end 609 | end 610 | end 611 | end 612 | end 613 | end 614 | if r1 615 | s0 << r1 616 | else 617 | break 618 | end 619 | end 620 | if s0.empty? 621 | @index = i0 622 | r0 = nil 623 | else 624 | r0 = instantiate_node(LinkAttributesContent,input, i0...index, s0) 625 | end 626 | 627 | node_cache[:link_attrs_content][start_index] = r0 628 | 629 | r0 630 | end 631 | 632 | def _nt_link_attr_text_unprotected 633 | start_index = index 634 | if node_cache[:link_attr_text_unprotected].has_key?(index) 635 | cached = node_cache[:link_attr_text_unprotected][index] 636 | if cached 637 | node_cache[:link_attr_text_unprotected][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 638 | @index = cached.interval.end 639 | end 640 | return cached 641 | end 642 | 643 | s0, i0 = [], index 644 | loop do 645 | if has_terminal?(@regexps[gr = '\A[^,\\]]'] ||= Regexp.new(gr), :regexp, index) 646 | r1 = true 647 | @index += 1 648 | else 649 | terminal_parse_failure('[^,\\]]') 650 | r1 = nil 651 | end 652 | if r1 653 | s0 << r1 654 | else 655 | break 656 | end 657 | end 658 | if s0.empty? 659 | @index = i0 660 | r0 = nil 661 | else 662 | r0 = instantiate_node(LinkText,input, i0...index, s0) 663 | end 664 | 665 | node_cache[:link_attr_text_unprotected][start_index] = r0 666 | 667 | r0 668 | end 669 | 670 | module LinkAttrTextProtected0 671 | def link_text_protected 672 | elements[1] 673 | end 674 | 675 | end 676 | 677 | def _nt_link_attr_text_protected 678 | start_index = index 679 | if node_cache[:link_attr_text_protected].has_key?(index) 680 | cached = node_cache[:link_attr_text_protected][index] 681 | if cached 682 | node_cache[:link_attr_text_protected][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 683 | @index = cached.interval.end 684 | end 685 | return cached 686 | end 687 | 688 | i0, s0 = index, [] 689 | if (match_len = has_terminal?('"', false, index)) 690 | r1 = true 691 | @index += match_len 692 | else 693 | terminal_parse_failure('\'"\'') 694 | r1 = nil 695 | end 696 | s0 << r1 697 | if r1 698 | r2 = _nt_link_text_protected 699 | s0 << r2 700 | if r2 701 | if (match_len = has_terminal?('"', false, index)) 702 | r3 = true 703 | @index += match_len 704 | else 705 | terminal_parse_failure('\'"\'') 706 | r3 = nil 707 | end 708 | s0 << r3 709 | end 710 | end 711 | if s0.last 712 | r0 = instantiate_node(LinkTextProtected,input, i0...index, s0) 713 | r0.extend(LinkAttrTextProtected0) 714 | else 715 | @index = i0 716 | r0 = nil 717 | end 718 | 719 | node_cache[:link_attr_text_protected][start_index] = r0 720 | 721 | r0 722 | end 723 | 724 | def _nt_link_text_protected 725 | start_index = index 726 | if node_cache[:link_text_protected].has_key?(index) 727 | cached = node_cache[:link_text_protected][index] 728 | if cached 729 | node_cache[:link_text_protected][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 730 | @index = cached.interval.end 731 | end 732 | return cached 733 | end 734 | 735 | s0, i0 = [], index 736 | loop do 737 | if has_terminal?(@regexps[gr = '\A[^"]'] ||= Regexp.new(gr), :regexp, index) 738 | r1 = true 739 | @index += 1 740 | else 741 | terminal_parse_failure('[^"]') 742 | r1 = nil 743 | end 744 | if r1 745 | s0 << r1 746 | else 747 | break 748 | end 749 | end 750 | if s0.empty? 751 | @index = i0 752 | r0 = nil 753 | else 754 | r0 = instantiate_node(LinkText,input, i0...index, s0) 755 | end 756 | 757 | node_cache[:link_text_protected][start_index] = r0 758 | 759 | r0 760 | end 761 | 762 | module LinkAttrRoleUnprotected0 763 | end 764 | 765 | def _nt_link_attr_role_unprotected 766 | start_index = index 767 | if node_cache[:link_attr_role_unprotected].has_key?(index) 768 | cached = node_cache[:link_attr_role_unprotected][index] 769 | if cached 770 | node_cache[:link_attr_role_unprotected][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 771 | @index = cached.interval.end 772 | end 773 | return cached 774 | end 775 | 776 | i0, s0 = index, [] 777 | if (match_len = has_terminal?('role=', false, index)) 778 | r1 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 779 | @index += match_len 780 | else 781 | terminal_parse_failure('\'role=\'') 782 | r1 = nil 783 | end 784 | s0 << r1 785 | if r1 786 | s2, i2 = [], index 787 | loop do 788 | if has_terminal?(@regexps[gr = '\A[^,\\]]'] ||= Regexp.new(gr), :regexp, index) 789 | r3 = true 790 | @index += 1 791 | else 792 | terminal_parse_failure('[^,\\]]') 793 | r3 = nil 794 | end 795 | if r3 796 | s2 << r3 797 | else 798 | break 799 | end 800 | end 801 | if s2.empty? 802 | @index = i2 803 | r2 = nil 804 | else 805 | r2 = instantiate_node(SyntaxNode,input, i2...index, s2) 806 | end 807 | s0 << r2 808 | end 809 | if s0.last 810 | r0 = instantiate_node(LinkRole,input, i0...index, s0) 811 | r0.extend(LinkAttrRoleUnprotected0) 812 | else 813 | @index = i0 814 | r0 = nil 815 | end 816 | 817 | node_cache[:link_attr_role_unprotected][start_index] = r0 818 | 819 | r0 820 | end 821 | 822 | module LinkAttrRoleProtected0 823 | end 824 | 825 | def _nt_link_attr_role_protected 826 | start_index = index 827 | if node_cache[:link_attr_role_protected].has_key?(index) 828 | cached = node_cache[:link_attr_role_protected][index] 829 | if cached 830 | node_cache[:link_attr_role_protected][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 831 | @index = cached.interval.end 832 | end 833 | return cached 834 | end 835 | 836 | i0, s0 = index, [] 837 | if (match_len = has_terminal?('role="', false, index)) 838 | r1 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 839 | @index += match_len 840 | else 841 | terminal_parse_failure('\'role="\'') 842 | r1 = nil 843 | end 844 | s0 << r1 845 | if r1 846 | s2, i2 = [], index 847 | loop do 848 | if has_terminal?(@regexps[gr = '\A[^"]'] ||= Regexp.new(gr), :regexp, index) 849 | r3 = true 850 | @index += 1 851 | else 852 | terminal_parse_failure('[^"]') 853 | r3 = nil 854 | end 855 | if r3 856 | s2 << r3 857 | else 858 | break 859 | end 860 | end 861 | if s2.empty? 862 | @index = i2 863 | r2 = nil 864 | else 865 | r2 = instantiate_node(SyntaxNode,input, i2...index, s2) 866 | end 867 | s0 << r2 868 | if r2 869 | if (match_len = has_terminal?('"', false, index)) 870 | r4 = true 871 | @index += match_len 872 | else 873 | terminal_parse_failure('\'"\'') 874 | r4 = nil 875 | end 876 | s0 << r4 877 | end 878 | end 879 | if s0.last 880 | r0 = instantiate_node(LinkRole,input, i0...index, s0) 881 | r0.extend(LinkAttrRoleProtected0) 882 | else 883 | @index = i0 884 | r0 = nil 885 | end 886 | 887 | node_cache[:link_attr_role_protected][start_index] = r0 888 | 889 | r0 890 | end 891 | 892 | module LinkAttrWindowUnprotected0 893 | end 894 | 895 | def _nt_link_attr_window_unprotected 896 | start_index = index 897 | if node_cache[:link_attr_window_unprotected].has_key?(index) 898 | cached = node_cache[:link_attr_window_unprotected][index] 899 | if cached 900 | node_cache[:link_attr_window_unprotected][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 901 | @index = cached.interval.end 902 | end 903 | return cached 904 | end 905 | 906 | i0, s0 = index, [] 907 | if (match_len = has_terminal?('window=', false, index)) 908 | r1 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 909 | @index += match_len 910 | else 911 | terminal_parse_failure('\'window=\'') 912 | r1 = nil 913 | end 914 | s0 << r1 915 | if r1 916 | s2, i2 = [], index 917 | loop do 918 | if has_terminal?(@regexps[gr = '\A[^,\\]]'] ||= Regexp.new(gr), :regexp, index) 919 | r3 = true 920 | @index += 1 921 | else 922 | terminal_parse_failure('[^,\\]]') 923 | r3 = nil 924 | end 925 | if r3 926 | s2 << r3 927 | else 928 | break 929 | end 930 | end 931 | if s2.empty? 932 | @index = i2 933 | r2 = nil 934 | else 935 | r2 = instantiate_node(SyntaxNode,input, i2...index, s2) 936 | end 937 | s0 << r2 938 | end 939 | if s0.last 940 | r0 = instantiate_node(LinkWindow,input, i0...index, s0) 941 | r0.extend(LinkAttrWindowUnprotected0) 942 | else 943 | @index = i0 944 | r0 = nil 945 | end 946 | 947 | node_cache[:link_attr_window_unprotected][start_index] = r0 948 | 949 | r0 950 | end 951 | 952 | module LinkAttrWindowProtected0 953 | end 954 | 955 | def _nt_link_attr_window_protected 956 | start_index = index 957 | if node_cache[:link_attr_window_protected].has_key?(index) 958 | cached = node_cache[:link_attr_window_protected][index] 959 | if cached 960 | node_cache[:link_attr_window_protected][index] = cached = SyntaxNode.new(input, index...(index + 1)) if cached == true 961 | @index = cached.interval.end 962 | end 963 | return cached 964 | end 965 | 966 | i0, s0 = index, [] 967 | if (match_len = has_terminal?('window="', false, index)) 968 | r1 = instantiate_node(SyntaxNode,input, index...(index + match_len)) 969 | @index += match_len 970 | else 971 | terminal_parse_failure('\'window="\'') 972 | r1 = nil 973 | end 974 | s0 << r1 975 | if r1 976 | s2, i2 = [], index 977 | loop do 978 | if has_terminal?(@regexps[gr = '\A[^"]'] ||= Regexp.new(gr), :regexp, index) 979 | r3 = true 980 | @index += 1 981 | else 982 | terminal_parse_failure('[^"]') 983 | r3 = nil 984 | end 985 | if r3 986 | s2 << r3 987 | else 988 | break 989 | end 990 | end 991 | if s2.empty? 992 | @index = i2 993 | r2 = nil 994 | else 995 | r2 = instantiate_node(SyntaxNode,input, i2...index, s2) 996 | end 997 | s0 << r2 998 | if r2 999 | if (match_len = has_terminal?('"', false, index)) 1000 | r4 = true 1001 | @index += match_len 1002 | else 1003 | terminal_parse_failure('\'"\'') 1004 | r4 = nil 1005 | end 1006 | s0 << r4 1007 | end 1008 | end 1009 | if s0.last 1010 | r0 = instantiate_node(LinkWindow,input, i0...index, s0) 1011 | r0.extend(LinkAttrWindowProtected0) 1012 | else 1013 | @index = i0 1014 | r0 = nil 1015 | end 1016 | 1017 | node_cache[:link_attr_window_protected][start_index] = r0 1018 | 1019 | r0 1020 | end 1021 | 1022 | end 1023 | 1024 | class AsciidoctorLinkGrammarParser < Treetop::Runtime::CompiledParser 1025 | include AsciidoctorLinkGrammar 1026 | end 1027 | 1028 | --------------------------------------------------------------------------------