coradoc-markdown 1.0.11 → 1.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/coradoc/markdown/parser/ast_processor.rb +7 -7
- data/lib/coradoc/markdown/parser/block_parser.rb +17 -12
- data/lib/coradoc/markdown/parser/inline_parser.rb +4 -3
- data/lib/coradoc/markdown/parser/parsanol_atoms.rb +135 -0
- data/lib/coradoc/markdown/transformer.rb +5 -5
- data/lib/coradoc/markdown/version.rb +1 -1
- data/lib/coradoc/markdown.rb +4 -3
- metadata +4 -4
- data/lib/coradoc/markdown/parser/parslet_extras.rb +0 -215
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 232eab66f6d33edae8a3cca57f5ebd16cca11ed96c4dd9a51611b9669410595b
|
|
4
|
+
data.tar.gz: 1b0f0051729a0e226826c66d49bed0c4227732491b0bbf6148f42df524b1e95f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d20d1132ed4f527557207c11736d3ad10289fecf47a480341ed3b768bee9b2e812195fcf77d377a374b9c82804044c8c2aa701688c7e08706070ef62f957545c
|
|
7
|
+
data.tar.gz: 24547cfb89cbe67a61f0bb63cee59495d2520d61df54ed03141963f5245e2cd5b1c537b80b2903a55b9cfa36d51639fc85f170a963686a6b71faff2a95845b11
|
|
@@ -84,8 +84,8 @@ module Coradoc
|
|
|
84
84
|
node.map { |child| process_node(child) }
|
|
85
85
|
when Hash
|
|
86
86
|
process_hash(node)
|
|
87
|
-
when
|
|
88
|
-
# Process escape sequences in
|
|
87
|
+
when Parsanol::Slice
|
|
88
|
+
# Process escape sequences in Parsanol::Slice values
|
|
89
89
|
process_escapes(node.to_s)
|
|
90
90
|
else
|
|
91
91
|
node
|
|
@@ -161,7 +161,7 @@ module Coradoc
|
|
|
161
161
|
return value if value.nil?
|
|
162
162
|
|
|
163
163
|
case value
|
|
164
|
-
when
|
|
164
|
+
when Parsanol::Slice
|
|
165
165
|
end
|
|
166
166
|
text = value.to_s
|
|
167
167
|
|
|
@@ -229,13 +229,13 @@ module Coradoc
|
|
|
229
229
|
result
|
|
230
230
|
end
|
|
231
231
|
|
|
232
|
-
# Process a text value (
|
|
232
|
+
# Process a text value (Parsanol::Slice or String)
|
|
233
233
|
# Only processes escape sequences without changing structure
|
|
234
234
|
def process_text_value(value)
|
|
235
235
|
return value if value.nil?
|
|
236
236
|
|
|
237
237
|
case value
|
|
238
|
-
when
|
|
238
|
+
when Parsanol::Slice
|
|
239
239
|
end
|
|
240
240
|
process_escapes(value.to_s)
|
|
241
241
|
end
|
|
@@ -374,7 +374,7 @@ module Coradoc
|
|
|
374
374
|
joined = extract_text_content(result)
|
|
375
375
|
{ ln: joined }
|
|
376
376
|
end
|
|
377
|
-
rescue
|
|
377
|
+
rescue Parsanol::ParseFailed
|
|
378
378
|
# If parsing fails, return original text in ln structure
|
|
379
379
|
{ ln: text }
|
|
380
380
|
end
|
|
@@ -441,7 +441,7 @@ module Coradoc
|
|
|
441
441
|
|
|
442
442
|
# Convert result back to string representation
|
|
443
443
|
inline_result_to_string(result)
|
|
444
|
-
rescue
|
|
444
|
+
rescue Parsanol::ParseFailed
|
|
445
445
|
content
|
|
446
446
|
end
|
|
447
447
|
end
|
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
module Coradoc
|
|
4
4
|
module Markdown
|
|
5
5
|
module Parser
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class BlockParser < Parslet::Parser
|
|
9
|
-
using ParsletExtras
|
|
6
|
+
# Load-time DSL side effect (sanctioned require_relative exception)
|
|
7
|
+
require_relative 'parsanol_atoms'
|
|
10
8
|
|
|
9
|
+
class BlockParser < Parsanol::Parser
|
|
11
10
|
# NOTE: Debug method for parser development. Outputs current parse position
|
|
12
11
|
# and capture context. Only called during parser debugging sessions.
|
|
13
12
|
def debug(msg)
|
|
@@ -32,7 +31,7 @@ module Coradoc
|
|
|
32
31
|
|
|
33
32
|
rule(:non_indent_space) { str(' ').repeat(0, 3) }
|
|
34
33
|
|
|
35
|
-
# Block nesting is the tricky part, but
|
|
34
|
+
# Block nesting is the tricky part, but Parsanol's `dynamic` and `scope`
|
|
36
35
|
# make it possible to be aware of what blocks we're already in, and implement
|
|
37
36
|
# a check for whether we're still inside of those blocks on the beginning of
|
|
38
37
|
# every line. The rules that match the line run inside of the innermost
|
|
@@ -53,11 +52,17 @@ module Coradoc
|
|
|
53
52
|
|
|
54
53
|
def open_block(kind, cont_rule)
|
|
55
54
|
dynamic do |_src, ctx|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
caps = ctx.captures
|
|
56
|
+
# Chain-read before the write: picks up the enclosing
|
|
57
|
+
# block's continuation rule when one is in scope (Parsanol's
|
|
58
|
+
# Scope#[] searches parent frames; #key? is chain-aware).
|
|
59
|
+
caps[:cont] =
|
|
60
|
+
if caps.key?(:cont)
|
|
61
|
+
caps[:cont] >> cont_rule
|
|
62
|
+
else
|
|
63
|
+
cont_rule
|
|
64
|
+
end
|
|
65
|
+
caps[:block] = kind
|
|
61
66
|
any.present? | any.absent?
|
|
62
67
|
end
|
|
63
68
|
end
|
|
@@ -727,7 +732,7 @@ module Coradoc
|
|
|
727
732
|
def self.parse(filename)
|
|
728
733
|
content = File.read(filename)
|
|
729
734
|
new.parse(content)
|
|
730
|
-
rescue
|
|
735
|
+
rescue Parsanol::ParseFailed => e
|
|
731
736
|
puts e.parse_failure_cause.ascii_tree
|
|
732
737
|
end
|
|
733
738
|
|
|
@@ -735,7 +740,7 @@ module Coradoc
|
|
|
735
740
|
def self.parse_with_processing(content)
|
|
736
741
|
ast = new.parse(content)
|
|
737
742
|
AstProcessor.process(ast)
|
|
738
|
-
rescue
|
|
743
|
+
rescue Parsanol::ParseFailed => e
|
|
739
744
|
puts e.parse_failure_cause.ascii_tree
|
|
740
745
|
nil
|
|
741
746
|
end
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
module Coradoc
|
|
4
4
|
module Markdown
|
|
5
5
|
module Parser
|
|
6
|
-
|
|
6
|
+
# Load-time DSL side effect (sanctioned require_relative exception)
|
|
7
|
+
require_relative 'parsanol_atoms'
|
|
8
|
+
|
|
7
9
|
autoload :HtmlEntities, "#{__dir__}/html_entities"
|
|
8
10
|
|
|
9
|
-
class InlineParser <
|
|
10
|
-
using ParsletExtras
|
|
11
|
+
class InlineParser < Parsanol::Parser
|
|
11
12
|
|
|
12
13
|
rule(:line_ending) { (str("\n") | str("\r\n") | str("\r")).ignore }
|
|
13
14
|
rule(:line_ending_or_eof) { line_ending | any.absent? }
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'parsanol'
|
|
4
|
+
|
|
5
|
+
module Coradoc
|
|
6
|
+
module Markdown
|
|
7
|
+
module Parser
|
|
8
|
+
# Custom grammar atoms and DSL surface for the Markdown parser,
|
|
9
|
+
# built on Parsanol::Atoms::Custom — the sanctioned extension
|
|
10
|
+
# point. Replaces the former parslet_extras module, which
|
|
11
|
+
# reached into Parslet internals via refinements.
|
|
12
|
+
module ParsanolAtoms
|
|
13
|
+
# Matches +parslet+ but yields a fixed +value+.
|
|
14
|
+
class Output < Parsanol::Atoms::Custom
|
|
15
|
+
attr_reader :parslet, :value
|
|
16
|
+
|
|
17
|
+
def initialize(parslet, value)
|
|
18
|
+
@parslet = parslet
|
|
19
|
+
@value = value
|
|
20
|
+
super()
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def try_match(source, context, consume_all)
|
|
24
|
+
success, = parslet.apply(source, context, consume_all)
|
|
25
|
+
[success, success ? value : nil]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Matches +parslet+ and yields the callable applied to the
|
|
30
|
+
# flattened match value.
|
|
31
|
+
class DynamicOutput < Parsanol::Atoms::Custom
|
|
32
|
+
include Parsanol::Atoms::CanFlatten
|
|
33
|
+
|
|
34
|
+
attr_reader :parslet, :callable
|
|
35
|
+
|
|
36
|
+
def initialize(parslet, callable)
|
|
37
|
+
@parslet = parslet
|
|
38
|
+
@callable = callable
|
|
39
|
+
super()
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def try_match(source, context, consume_all)
|
|
43
|
+
success, value = parslet.apply(source, context, consume_all)
|
|
44
|
+
return [false, nil] unless success
|
|
45
|
+
|
|
46
|
+
[true, callable.call(flatten(value))]
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Positive/negative lookbehind: succeeds (zero-width) when the
|
|
51
|
+
# preceding +number+ characters are/are not matched by
|
|
52
|
+
# +bound_parslet+. Character-accurate for multibyte input —
|
|
53
|
+
# rewinds over UTF-8 continuation bytes.
|
|
54
|
+
class Lookbehind < Parsanol::Atoms::Custom
|
|
55
|
+
attr_reader :positive, :number, :bound_parslet
|
|
56
|
+
|
|
57
|
+
def initialize(bound_parslet, number, positive: true)
|
|
58
|
+
@positive = positive
|
|
59
|
+
@number = number
|
|
60
|
+
@bound_parslet = bound_parslet
|
|
61
|
+
super()
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def error_msgs
|
|
65
|
+
@error_msgs ||= {
|
|
66
|
+
positive: ['Input should be preceded by ', bound_parslet],
|
|
67
|
+
negative: ['Input should not be preceded by ', bound_parslet]
|
|
68
|
+
}
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def try_match(source, context, _consume_all)
|
|
72
|
+
rewind_pos = source.bytepos
|
|
73
|
+
if rewind_pos.zero?
|
|
74
|
+
return [true, nil] unless positive
|
|
75
|
+
|
|
76
|
+
return context.err_at(self, source, error_msgs[:positive], source.pos)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
source.bytepos = rewind_chars(source, number)
|
|
80
|
+
error_pos = source.pos
|
|
81
|
+
success, = bound_parslet.apply(source, context, false)
|
|
82
|
+
|
|
83
|
+
if positive
|
|
84
|
+
return [true, nil] if success
|
|
85
|
+
|
|
86
|
+
context.err_at(self, source, error_msgs[:positive], error_pos)
|
|
87
|
+
elsif success
|
|
88
|
+
context.err_at(self, source, error_msgs[:negative], error_pos)
|
|
89
|
+
else
|
|
90
|
+
[true, nil]
|
|
91
|
+
end
|
|
92
|
+
ensure
|
|
93
|
+
source.bytepos = rewind_pos
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def rewind_chars(source, nchars)
|
|
99
|
+
input = source.input
|
|
100
|
+
pos = source.bytepos
|
|
101
|
+
nchars.times do
|
|
102
|
+
pos -= 1
|
|
103
|
+
pos -= 1 while pos.positive? && (input.getbyte(pos) & 0xC0) == 0x80
|
|
104
|
+
end
|
|
105
|
+
[pos, 0].max
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Additive DSL surface (no overrides): exposes the custom atoms on
|
|
114
|
+
# every atom the way the former parslet_extras refinements did.
|
|
115
|
+
module Parsanol
|
|
116
|
+
module Atoms
|
|
117
|
+
module DSL
|
|
118
|
+
def output(value)
|
|
119
|
+
Coradoc::Markdown::Parser::ParsanolAtoms::Output.new(self, value)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def dynamic_output(callable)
|
|
123
|
+
Coradoc::Markdown::Parser::ParsanolAtoms::DynamicOutput.new(self, callable)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def precedes?(num = 1)
|
|
127
|
+
Coradoc::Markdown::Parser::ParsanolAtoms::Lookbehind.new(self, num, positive: true)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def does_not_precede?(num = 1)
|
|
131
|
+
Coradoc::Markdown::Parser::ParsanolAtoms::Lookbehind.new(self, num, positive: false)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require '
|
|
3
|
+
require 'parsanol'
|
|
4
4
|
|
|
5
5
|
module Coradoc
|
|
6
6
|
module Markdown
|
|
7
7
|
autoload :ParserUtil, "#{__dir__}/parser_util"
|
|
8
8
|
|
|
9
|
-
# Transformer converts
|
|
9
|
+
# Transformer converts Parsanol AST into Markdown Document Model objects.
|
|
10
10
|
#
|
|
11
11
|
# This transformer takes the raw output from the BlockParser/InlineParser
|
|
12
12
|
# and converts it into semantic model objects (Heading, Paragraph, etc.)
|
|
13
13
|
#
|
|
14
|
-
class Transformer <
|
|
14
|
+
class Transformer < Parsanol::Transform
|
|
15
15
|
# ATX Heading: # Heading
|
|
16
16
|
rule(heading: simple(:heading), text: simple(:text)) do
|
|
17
17
|
Heading.new(level: heading.to_s.length, text: text.to_s.strip)
|
|
@@ -198,7 +198,7 @@ module Coradoc
|
|
|
198
198
|
when Array
|
|
199
199
|
# Transform each item
|
|
200
200
|
element.map { |e| transform_element(e) }.compact
|
|
201
|
-
when
|
|
201
|
+
when Parsanol::Slice
|
|
202
202
|
Text.new(content: element.to_s)
|
|
203
203
|
else
|
|
204
204
|
Text.new(content: element.to_s)
|
|
@@ -536,7 +536,7 @@ module Coradoc
|
|
|
536
536
|
else
|
|
537
537
|
content.values.map { |v| extract_text(v) }.join
|
|
538
538
|
end
|
|
539
|
-
when
|
|
539
|
+
when Parsanol::Slice
|
|
540
540
|
content.to_s
|
|
541
541
|
else
|
|
542
542
|
content.to_s
|
data/lib/coradoc/markdown.rb
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# This gem provides Markdown support for the Coradoc document processing library.
|
|
6
6
|
# It includes:
|
|
7
7
|
# - Markdown Document Model (Coradoc::Markdown::*)
|
|
8
|
-
# - Markdown Parser (CommonMark-compliant,
|
|
8
|
+
# - Markdown Parser (CommonMark-compliant, Parsanol-based)
|
|
9
9
|
# - Markdown Serializer (round-trip capable)
|
|
10
10
|
# - Kramdown extensions support (IAL, ALD, math, TOC)
|
|
11
11
|
#
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
# # Serialize back to Markdown
|
|
19
19
|
# output = Coradoc::Markdown.serialize(document)
|
|
20
20
|
|
|
21
|
-
require '
|
|
21
|
+
require 'parsanol'
|
|
22
|
+
require 'parsanol/convenience'
|
|
22
23
|
require 'lutaml/model'
|
|
23
24
|
|
|
24
25
|
# Coradoc document transformation library.
|
|
@@ -91,7 +92,7 @@ module Coradoc
|
|
|
91
92
|
module Parser
|
|
92
93
|
autoload :BlockParser, 'coradoc/markdown/parser/block_parser'
|
|
93
94
|
autoload :InlineParser, 'coradoc/markdown/parser/inline_parser'
|
|
94
|
-
autoload :
|
|
95
|
+
autoload :ParsanolAtoms, 'coradoc/markdown/parser/parsanol_atoms'
|
|
95
96
|
autoload :HTML_ENTITIES, 'coradoc/markdown/parser/html_entities'
|
|
96
97
|
autoload :AstProcessor, 'coradoc/markdown/parser/ast_processor'
|
|
97
98
|
autoload :FrontmatterParser, 'coradoc/markdown/parser/frontmatter_parser'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: coradoc-markdown
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.12
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -38,7 +38,7 @@ dependencies:
|
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: 0.8.0
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
|
-
name:
|
|
41
|
+
name: parsanol
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
44
|
- - ">="
|
|
@@ -94,7 +94,7 @@ dependencies:
|
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
95
|
version: '0'
|
|
96
96
|
description: Provides Markdown parsing and serialization capabilities for Coradoc.
|
|
97
|
-
Includes the Markdown Document Model, a CommonMark-compliant
|
|
97
|
+
Includes the Markdown Document Model, a CommonMark-compliant Parsanol-based parser,
|
|
98
98
|
and round-trip capable serializer.
|
|
99
99
|
email:
|
|
100
100
|
- open.source@ribose.com
|
|
@@ -152,7 +152,7 @@ files:
|
|
|
152
152
|
- lib/coradoc/markdown/parser/frontmatter_parser.rb
|
|
153
153
|
- lib/coradoc/markdown/parser/html_entities.rb
|
|
154
154
|
- lib/coradoc/markdown/parser/inline_parser.rb
|
|
155
|
-
- lib/coradoc/markdown/parser/
|
|
155
|
+
- lib/coradoc/markdown/parser/parsanol_atoms.rb
|
|
156
156
|
- lib/coradoc/markdown/parser_util.rb
|
|
157
157
|
- lib/coradoc/markdown/serializer.rb
|
|
158
158
|
- lib/coradoc/markdown/serializer/builder.rb
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'parslet'
|
|
4
|
-
require 'parslet/convenience'
|
|
5
|
-
|
|
6
|
-
module Coradoc
|
|
7
|
-
module Markdown
|
|
8
|
-
module Parser
|
|
9
|
-
module ParsletExtras
|
|
10
|
-
refine Parslet::Source do
|
|
11
|
-
def rewind(nchars)
|
|
12
|
-
# https://github.com/ruby/strscan/issues/122
|
|
13
|
-
self.charpos = @str.charpos - nchars
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def charpos=(pos)
|
|
17
|
-
@str.reset
|
|
18
|
-
@str.getch while @str.charpos < pos
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def charpos
|
|
22
|
-
@str.charpos
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def peek_byte
|
|
26
|
-
@str.peek(1)
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
refine Parslet::Scope do
|
|
31
|
-
attr_reader :current
|
|
32
|
-
|
|
33
|
-
def key?(...)
|
|
34
|
-
@current.key?(...)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def has_key?(...)
|
|
38
|
-
@current.key?(...)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def root
|
|
42
|
-
scope = current
|
|
43
|
-
scope = scope.parent while scope.parent
|
|
44
|
-
scope
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
refine Parslet::Scope::Binding do
|
|
49
|
-
def key?(...)
|
|
50
|
-
@hash.key?(...)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def has_key?(...)
|
|
54
|
-
@hash.key?(...)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def initialize_copy(original)
|
|
58
|
-
super
|
|
59
|
-
@hash = @hash.clone
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# like Named but returning other things
|
|
64
|
-
class Output < Parslet::Atoms::Base
|
|
65
|
-
attr_reader :parslet, :value
|
|
66
|
-
|
|
67
|
-
def initialize(parslet, value)
|
|
68
|
-
super()
|
|
69
|
-
|
|
70
|
-
@parslet = parslet
|
|
71
|
-
@value = value
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def apply(source, context, consume_all)
|
|
75
|
-
success, = result = parslet.apply(source, context, consume_all)
|
|
76
|
-
|
|
77
|
-
return result unless success
|
|
78
|
-
|
|
79
|
-
succ(@value)
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def to_s_inner(prec)
|
|
83
|
-
"#{value}:#{parslet.to_s(prec)}"
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
class DynamicOutput < Parslet::Atoms::Base
|
|
88
|
-
attr_reader :parslet, :callable
|
|
89
|
-
|
|
90
|
-
def initialize(parslet, callable)
|
|
91
|
-
super()
|
|
92
|
-
|
|
93
|
-
@parslet = parslet
|
|
94
|
-
@callable = callable
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def apply(source, context, consume_all)
|
|
98
|
-
success, value = result = parslet.apply(source, context, consume_all)
|
|
99
|
-
|
|
100
|
-
return result unless success
|
|
101
|
-
|
|
102
|
-
succ(@callable.call(flatten(value)))
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def to_s_inner(prec)
|
|
106
|
-
"#{callable}:#{parslet.to_s(prec)}"
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
class Lookbehind < Parslet::Atoms::Base
|
|
111
|
-
using ParsletExtras
|
|
112
|
-
attr_reader :positive
|
|
113
|
-
attr_reader :number, :bound_parslet
|
|
114
|
-
|
|
115
|
-
def initialize(bound_parslet, number, positive: true)
|
|
116
|
-
super()
|
|
117
|
-
|
|
118
|
-
# Model positive and negative lookbehind by testing this flag.
|
|
119
|
-
@positive = positive
|
|
120
|
-
@number = number
|
|
121
|
-
@bound_parslet = bound_parslet
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def error_msgs
|
|
125
|
-
@error_msgs ||= {
|
|
126
|
-
positive: ['Input should be preceded by ', bound_parslet],
|
|
127
|
-
negative: ['Input should not be preceded by ', bound_parslet]
|
|
128
|
-
}
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def try(source, context, consume_all)
|
|
132
|
-
rewind_pos = source.bytepos
|
|
133
|
-
if source.bytepos.zero?
|
|
134
|
-
return succ(nil) unless positive
|
|
135
|
-
|
|
136
|
-
return context.err_at(self, source, error_msgs[:positive], source.pos)
|
|
137
|
-
end
|
|
138
|
-
source.rewind(number)
|
|
139
|
-
error_pos = source.pos
|
|
140
|
-
|
|
141
|
-
success, = bound_parslet.apply(source, context, consume_all)
|
|
142
|
-
|
|
143
|
-
if positive
|
|
144
|
-
return succ(nil) if success
|
|
145
|
-
|
|
146
|
-
context.err_at(self, source, error_msgs[:positive], error_pos)
|
|
147
|
-
else
|
|
148
|
-
return succ(nil) unless success
|
|
149
|
-
|
|
150
|
-
context.err_at(self, source, error_msgs[:negative], error_pos)
|
|
151
|
-
end
|
|
152
|
-
ensure
|
|
153
|
-
source.bytepos = rewind_pos
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
def to_s_inner(prec)
|
|
157
|
-
@char = positive ? '&' : '!'
|
|
158
|
-
"<#{@char}<#{number}<#{bound_parslet.to_s(prec)}"
|
|
159
|
-
end
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
# Like Dynamic but does not return a further parslet, just a reject/accept boolean
|
|
163
|
-
module ::Parslet
|
|
164
|
-
module Atoms
|
|
165
|
-
class Check < ::Parslet::Atoms::Base
|
|
166
|
-
attr_reader :block
|
|
167
|
-
|
|
168
|
-
def initialize(block)
|
|
169
|
-
super()
|
|
170
|
-
@block = block
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
def cached?
|
|
174
|
-
false
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
def try(source, context, _consume_all)
|
|
178
|
-
[block.call(source, context), nil]
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
def to_s_inner(_prec)
|
|
182
|
-
'check { ... }'
|
|
183
|
-
end
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
refine ::Parslet do
|
|
189
|
-
def check(&block)
|
|
190
|
-
::Parslet::Atoms::Check.new(block)
|
|
191
|
-
end
|
|
192
|
-
module_function :check
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
refine ::Parslet::Atoms::DSL do
|
|
196
|
-
def output(value)
|
|
197
|
-
Output.new(self, value)
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
def dynamic_output(value)
|
|
201
|
-
DynamicOutput.new(self, value)
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
def precedes?(num = 1)
|
|
205
|
-
Lookbehind.new(self, num, positive: true)
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
def does_not_precede?(num = 1)
|
|
209
|
-
Lookbehind.new(self, num, positive: false)
|
|
210
|
-
end
|
|
211
|
-
end
|
|
212
|
-
end
|
|
213
|
-
end
|
|
214
|
-
end
|
|
215
|
-
end
|