d-mark 1.0.0a1 → 1.0.0b2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/Gemfile.lock +61 -58
- data/NEWS.md +36 -0
- data/README.md +20 -0
- data/Rakefile +3 -3
- data/d-mark.gemspec +3 -5
- data/ideas.dmark +17 -0
- data/lib/d-mark/parser.rb +56 -71
- data/lib/d-mark/translator.rb +42 -13
- data/lib/d-mark/version.rb +1 -1
- data/samples/trivial.dmark +1 -1
- data/spec/d-mark/parser_spec.rb +150 -48
- data/spec/d-mark/translator_spec.rb +74 -18
- metadata +12 -35
- data/README.adoc +0 -221
- data/lib/d-mark/cli.rb +0 -28
- data/samples/identifiers-and-patterns.dmark +0 -539
@@ -1,5 +1,5 @@
|
|
1
1
|
describe DMark::Translator do
|
2
|
-
let(:translator) { translator_class.new
|
2
|
+
let(:translator) { translator_class.new }
|
3
3
|
let(:translator_class) { described_class }
|
4
4
|
|
5
5
|
let(:nodes) do
|
@@ -7,39 +7,95 @@ describe DMark::Translator do
|
|
7
7
|
DMark::ElementNode.new(
|
8
8
|
'para',
|
9
9
|
{ 'only' => 'web', 'animal' => 'donkey' },
|
10
|
-
[
|
10
|
+
[
|
11
|
+
DMark::ElementNode.new('em', {}, ['Hello']),
|
12
|
+
' world!'
|
13
|
+
]
|
11
14
|
)
|
12
15
|
]
|
13
16
|
end
|
14
17
|
|
15
|
-
|
16
|
-
subject { translator.run }
|
17
|
-
|
18
|
+
shared_examples 'translates' do
|
18
19
|
context 'translator base class' do
|
19
|
-
it 'raises
|
20
|
-
expect { subject }.to raise_error(
|
20
|
+
it 'raises error' do
|
21
|
+
expect { subject }.to raise_error(
|
22
|
+
DMark::Translator::UnhandledNode,
|
23
|
+
'Unhandled element node "para"'
|
24
|
+
)
|
21
25
|
end
|
22
26
|
end
|
23
27
|
|
24
28
|
context 'custom translator' do
|
25
29
|
let(:translator_class) do
|
26
30
|
Class.new(described_class) do
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
31
|
+
def handle_string(string, _context)
|
32
|
+
[string]
|
33
|
+
end
|
34
|
+
|
35
|
+
def handle_element(element, context)
|
36
|
+
[
|
37
|
+
"<#{element.name}",
|
38
|
+
element.attributes.map { |k, v| ' ' + [k, v].join('=') }.join,
|
39
|
+
'>',
|
40
|
+
handle_children(element, context),
|
41
|
+
"</#{element.name}>"
|
42
|
+
]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it { is_expected.to eql('<para only=web animal=donkey><em>Hello</em> world!</para>') }
|
48
|
+
|
49
|
+
context 'doing something with context' do
|
50
|
+
let(:translator_class) do
|
51
|
+
Class.new(described_class) do
|
52
|
+
def handle_string(string, context)
|
53
|
+
[string, " [parent=#{context[:kind]}]"]
|
54
|
+
end
|
55
|
+
|
56
|
+
def handle_element(element, context)
|
57
|
+
[
|
58
|
+
"<#{element.name}",
|
59
|
+
element.attributes.map { |k, v| ' ' + [k, v].join('=') }.join,
|
60
|
+
'>',
|
61
|
+
handle_children(element, context.merge(kind: element.name)),
|
62
|
+
"</#{element.name}>"
|
63
|
+
]
|
37
64
|
end
|
38
65
|
end
|
39
66
|
end
|
67
|
+
|
68
|
+
it { is_expected.to eql('<para only=web animal=donkey><em>Hello [parent=em]</em> world! [parent=para]</para>') }
|
40
69
|
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
shared_examples 'errors on unknown type' do
|
74
|
+
it 'raises' do
|
75
|
+
expect { subject }.to raise_error(
|
76
|
+
ArgumentError,
|
77
|
+
'Cannot handle Symbol'
|
78
|
+
)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '.translate' do
|
83
|
+
subject { translator_class.translate(nodes) }
|
84
|
+
include_examples 'translates'
|
85
|
+
|
86
|
+
context 'unrecognised type' do
|
87
|
+
subject { translator_class.translate([:donkey]) }
|
88
|
+
include_examples 'errors on unknown type'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe '#translate' do
|
93
|
+
subject { translator.translate(nodes) }
|
94
|
+
include_examples 'translates'
|
41
95
|
|
42
|
-
|
96
|
+
context 'unrecognised type' do
|
97
|
+
subject { translator.translate([:donkey]) }
|
98
|
+
include_examples 'errors on unknown type'
|
43
99
|
end
|
44
100
|
end
|
45
101
|
end
|
metadata
CHANGED
@@ -1,35 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: d-mark
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.0b2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Denis Defreyne
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 1.11.2
|
20
|
-
- - "<"
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: '2.0'
|
23
|
-
type: :development
|
24
|
-
prerelease: false
|
25
|
-
version_requirements: !ruby/object:Gem::Requirement
|
26
|
-
requirements:
|
27
|
-
- - ">="
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: 1.11.2
|
30
|
-
- - "<"
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: '2.0'
|
11
|
+
date: 2021-01-01 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
33
13
|
description: D★Mark is a markup language aimed at being able to write semantically
|
34
14
|
meaningful text without limiting itself to the semantics provided by HTML or Markdown.
|
35
15
|
email: denis.defreyne@stoneship.org
|
@@ -37,7 +17,7 @@ executables: []
|
|
37
17
|
extensions: []
|
38
18
|
extra_rdoc_files:
|
39
19
|
- LICENSE
|
40
|
-
- README.
|
20
|
+
- README.md
|
41
21
|
- NEWS.md
|
42
22
|
files:
|
43
23
|
- Gemfile
|
@@ -45,16 +25,15 @@ files:
|
|
45
25
|
- Guardfile
|
46
26
|
- LICENSE
|
47
27
|
- NEWS.md
|
48
|
-
- README.
|
28
|
+
- README.md
|
49
29
|
- Rakefile
|
50
30
|
- d-mark.gemspec
|
31
|
+
- ideas.dmark
|
51
32
|
- lib/d-mark.rb
|
52
|
-
- lib/d-mark/cli.rb
|
53
33
|
- lib/d-mark/element_node.rb
|
54
34
|
- lib/d-mark/parser.rb
|
55
35
|
- lib/d-mark/translator.rb
|
56
36
|
- lib/d-mark/version.rb
|
57
|
-
- samples/identifiers-and-patterns.dmark
|
58
37
|
- samples/trivial.dmark
|
59
38
|
- samples/trivial.rb
|
60
39
|
- spec/d-mark/element_node_spec.rb
|
@@ -65,27 +44,25 @@ homepage: http://rubygems.org/gems/d-mark
|
|
65
44
|
licenses:
|
66
45
|
- MIT
|
67
46
|
metadata: {}
|
68
|
-
post_install_message:
|
47
|
+
post_install_message:
|
69
48
|
rdoc_options:
|
70
49
|
- "--main"
|
71
|
-
- README.
|
50
|
+
- README.md
|
72
51
|
require_paths:
|
73
52
|
- lib
|
74
53
|
required_ruby_version: !ruby/object:Gem::Requirement
|
75
54
|
requirements:
|
76
55
|
- - ">="
|
77
56
|
- !ruby/object:Gem::Version
|
78
|
-
version: 2.
|
57
|
+
version: '2.5'
|
79
58
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
59
|
requirements:
|
81
60
|
- - ">"
|
82
61
|
- !ruby/object:Gem::Version
|
83
62
|
version: 1.3.1
|
84
63
|
requirements: []
|
85
|
-
|
86
|
-
|
87
|
-
signing_key:
|
64
|
+
rubygems_version: 3.2.4
|
65
|
+
signing_key:
|
88
66
|
specification_version: 4
|
89
67
|
summary: markup language for writing text
|
90
68
|
test_files: []
|
91
|
-
has_rdoc:
|
data/README.adoc
DELETED
@@ -1,221 +0,0 @@
|
|
1
|
-
= D★Mark
|
2
|
-
Denis Defreyne <denis@stoneship.org>
|
3
|
-
|
4
|
-
image:http://img.shields.io/gem/v/d-mark.svg[Gem version, link="http://rubygems.org/gems/d-mark"]
|
5
|
-
image:http://img.shields.io/travis/ddfreyne/d-mark.svg[Build status, link="https://travis-ci.org/ddfreyne/d-mark"]
|
6
|
-
image:http://img.shields.io/codeclimate/github/ddfreyne/d-mark.svg[Code Climate, link="https://codeclimate.com/github/ddfreyne/d-mark"]
|
7
|
-
image:http://img.shields.io/codecov/c/github/ddfreyne/d-mark.svg[Code Coverage, link="https://codecov.io/github/ddfreyne/d-mark"]
|
8
|
-
|
9
|
-
_D★Mark_ is a language for marking up prose. It facilitates writing semantically meaningful text, without limiting itself to the semantics provided by HTML or Markdown.
|
10
|
-
|
11
|
-
Here’s an example of D★Mark:
|
12
|
-
|
13
|
-
[source]
|
14
|
-
----
|
15
|
-
h2. Patterns
|
16
|
-
|
17
|
-
para. Patterns are used to find items and layouts based on their identifier. They come in three varieties:
|
18
|
-
|
19
|
-
list[unordered].
|
20
|
-
item. glob patterns
|
21
|
-
item. regular expression patterns
|
22
|
-
item. legacy patterns
|
23
|
-
|
24
|
-
para. A glob pattern that matches every item is %pattern{/**/*}. A glob pattern that matches every item/layout with the extension %filename{md} is %glob{/**/*.md}.
|
25
|
-
----
|
26
|
-
|
27
|
-
== Samples
|
28
|
-
|
29
|
-
The `samples/` directory contains some sample D★Mark files. They can be processed by invoking the appropriate script with the same filename. For example:
|
30
|
-
|
31
|
-
....
|
32
|
-
% bundle exec ruby samples/trivial.rb
|
33
|
-
<p>I’m a <em>trivial</em> example!</p>
|
34
|
-
....
|
35
|
-
|
36
|
-
== Structure of a D★Mark document
|
37
|
-
|
38
|
-
_D★Mark_ knows two constructs:
|
39
|
-
|
40
|
-
Block-level elements::
|
41
|
-
Every non-blank line of a D★Mark document corresponds to a block. A block can be a paragraph, a list, a header, a source code listing, or more. They start with the name of the element, a period, a space character, followed by the content. For example:
|
42
|
-
+
|
43
|
-
[source]
|
44
|
-
----
|
45
|
-
para. Patterns are used to find items and layouts based on their identifier. They come in three varieties.
|
46
|
-
----
|
47
|
-
|
48
|
-
Inline elements::
|
49
|
-
Inside a block, text can be marked up using inline elements, which start with a percentage sign, the name of the element, and the content within braces. For example, `%emph{crazy}` is an `emph` element with the content `crazy`.
|
50
|
-
|
51
|
-
Block-level elements can be nested. To do so, indent the nested block two spaces deeper than the enclosing block. For example, the following defines a `list` element with three `item` elements inside it:
|
52
|
-
|
53
|
-
[source]
|
54
|
-
----
|
55
|
-
list[unordered].
|
56
|
-
item. glob patterns
|
57
|
-
item. regular expression patterns
|
58
|
-
item. legacy patterns
|
59
|
-
----
|
60
|
-
|
61
|
-
Block-level elements can also include plain text. In this case, the content is not wrapped inside a nested block-level element. This is particularly useful for source code listing. For example:
|
62
|
-
|
63
|
-
[source]
|
64
|
-
----
|
65
|
-
listing[lang=ruby].
|
66
|
-
identifier = Nanoc::Identifier.new('/about.md')
|
67
|
-
|
68
|
-
identifier.without_ext
|
69
|
-
# => "/about"
|
70
|
-
|
71
|
-
identifier.ext
|
72
|
-
# => "md"
|
73
|
-
----
|
74
|
-
|
75
|
-
Block-level elements and inline elements are identical in the tree representation of D★Mark. This means that any inline element can be rewritten as a block-level element.
|
76
|
-
|
77
|
-
NOTE: To do: Elaborate on the distinction and similarity of block-level and inline elements.
|
78
|
-
|
79
|
-
NOTE: To do: Describe escaping rules.
|
80
|
-
|
81
|
-
=== Attributes
|
82
|
-
|
83
|
-
Both block and inline elements can also have attributes. Attributes are enclosed in square brackets after the element name, as a comma-separated list of key-value pairs separated by an equal sign. The value part, along with the equal sign, can be omitted, in which case the value will be equal to the key name.
|
84
|
-
|
85
|
-
For example:
|
86
|
-
|
87
|
-
* `%code[lang=ruby]{Nanoc::VERSION}` is an inline `code` element with the `lang` attribute set to `ruby`.
|
88
|
-
|
89
|
-
* `%only[web]{Refer to the release notes for details.}` is an inline `only` element with the `web` attribute set to `web`.
|
90
|
-
|
91
|
-
* `h2[id=donkey]. All about donkeys` is a block-level `h2` element with the `id` attribute set to `donkey`.
|
92
|
-
|
93
|
-
* `p[print]. This is a paragraph that only readers of the book will see.` is a block-level `para` element with the `print` attribute set to `print`.
|
94
|
-
|
95
|
-
NOTE: The behavior of keys with missing values might change to default to booleans rather than to the key name.
|
96
|
-
|
97
|
-
== Goals
|
98
|
-
|
99
|
-
Be extensible::
|
100
|
-
D★Mark defines only the syntax of the markup language, and doesn’t bother with semantics. It does not prescribe which element names are valid in the context of a vocabulary, because it does not come with a vocabulary.
|
101
|
-
|
102
|
-
Be simple::
|
103
|
-
Simplicity implies being easy to write and easy to parse. D★Mark eschews ambiguity and aims to have a short formal syntactical definition. This also means that it is easy to syntax highlight.
|
104
|
-
|
105
|
-
Be compact::
|
106
|
-
Introduce as little extra syntax as possible.
|
107
|
-
|
108
|
-
== Comparison with other languages
|
109
|
-
|
110
|
-
D★Mark takes inspiration from a variety of other languages.
|
111
|
-
|
112
|
-
HTML::
|
113
|
-
HTML is syntactically unambiguous, but comparatively more verbose than other languages. It also prescribes only a small set of elements, which makes it awkward to use for prose that requires more thorough markup. It is possible use `span` or `div` elements with custom classes, but this approach turns an already verbose language into something even more verbose.
|
114
|
-
+
|
115
|
-
[source,html]
|
116
|
-
----
|
117
|
-
<p>A glob pattern that matches every item is <span class="pattern attr-kind-glob">/**/*</span>.</p>
|
118
|
-
----
|
119
|
-
+
|
120
|
-
[source,d-mark]
|
121
|
-
----
|
122
|
-
para. A glob pattern that matches every item is %pattern[glob]{/**/*}.
|
123
|
-
----
|
124
|
-
|
125
|
-
XML::
|
126
|
-
Similar to HTML, with the major difference that XML does not prescribe a set of elements.
|
127
|
-
+
|
128
|
-
[source,xml]
|
129
|
-
----
|
130
|
-
<para>A glob pattern that matches every item is <pattern kind="glob">/**/*</pattern>.</para>
|
131
|
-
----
|
132
|
-
+
|
133
|
-
[source,d-mark]
|
134
|
-
----
|
135
|
-
para. A glob pattern that matches every item is %pattern[glob]{/**/*}.
|
136
|
-
----
|
137
|
-
|
138
|
-
Markdown::
|
139
|
-
Markdown has a compact syntax, but is complex and ambiguous, as evidenced by the many different mutually incompatible implementations. It prescribes a small set of elements (smaller even than HTML). It supports embedding raw HTML, which in theory makes it possible to combine the best of both worlds, but in practice leads to markup that is harder to read than either Markdown or HTML separately, and occasionally trips up the parser and syntax highlighter.
|
140
|
-
+
|
141
|
-
[source]
|
142
|
-
----
|
143
|
-
A glob pattern that matches every item is <span class="glob attr-kind-glob">/**/*</span>.
|
144
|
-
----
|
145
|
-
+
|
146
|
-
[source,d-mark]
|
147
|
-
----
|
148
|
-
para. A glob pattern that matches every item is %pattern[glob]{/**/*}.
|
149
|
-
----
|
150
|
-
|
151
|
-
AsciiDoc::
|
152
|
-
AsciiDoc, along with its AsciiDoctor variant, are syntactically unambiguous, but complex languages. They prescribe a comparatively large set of elements which translates well to DocBook and HTML. They do not support custom markup or embedding raw HTML, which makes them harder t use for prose that requires more complex markup.
|
153
|
-
+
|
154
|
-
_(No example, as this example cannot be represented with AsciiDoc.)_
|
155
|
-
|
156
|
-
TeX, LaTeX::
|
157
|
-
TeX is a turing-complete programming language, as opposed to a markup language, intended for typesetting. This makes it impractical for using it as the source for converting it to other formats. Its syntax is simple and compact, and served as an inspiration for D★Mark.
|
158
|
-
+
|
159
|
-
[source,latex]
|
160
|
-
----
|
161
|
-
A glob pattern that matches every item is \pattern[glob]{/**/*}.
|
162
|
-
----
|
163
|
-
+
|
164
|
-
[source,d-mark]
|
165
|
-
----
|
166
|
-
para. A glob pattern that matches every item is %pattern[glob]{/**/*}.
|
167
|
-
----
|
168
|
-
|
169
|
-
JSON, YAML::
|
170
|
-
JSON and YAML are data interchange formats rather than markup languages, and thus are not well-suited for marking up prose.
|
171
|
-
+
|
172
|
-
[source,json]
|
173
|
-
----
|
174
|
-
[
|
175
|
-
"A glob pattern that matches every item is ",
|
176
|
-
["pattern", {"kind": "glob"}, ["/**/*"]],
|
177
|
-
"."
|
178
|
-
]
|
179
|
-
----
|
180
|
-
+
|
181
|
-
[source,d-mark]
|
182
|
-
----
|
183
|
-
para. A glob pattern that matches every item is %pattern[glob]{/**/*}.
|
184
|
-
----
|
185
|
-
|
186
|
-
== Specification
|
187
|
-
|
188
|
-
NOTE: To do: write this section.
|
189
|
-
|
190
|
-
== Programmatic usage
|
191
|
-
|
192
|
-
Handling a D★Mark file consists of two stages: parsing and translating.
|
193
|
-
|
194
|
-
The parsing stage converts text into a list of nodes. Construct a parser with the tokens as input, and call `#run` to get the list of nodes.
|
195
|
-
|
196
|
-
[source,ruby]
|
197
|
-
----
|
198
|
-
content = File.read(ARGV[0])
|
199
|
-
nodes = DMark::Parser.new(content).run
|
200
|
-
----
|
201
|
-
|
202
|
-
The translating stage is not the responsibility of D★Mark. A translator is part of the domain of the source text, and D★Mark only deals with syntax rather than semantics. A translator will run over the tree and convert it into something else (usually another string). To do so, handle each node type (`DMark::ElementNode` or `String`). For example, the following translator will convert the tree into something that resembles XML:
|
203
|
-
|
204
|
-
[source,ruby]
|
205
|
-
----
|
206
|
-
class MyXMLLikeTranslator < DMark::Translator
|
207
|
-
def handle(node)
|
208
|
-
case node
|
209
|
-
when String
|
210
|
-
out << node
|
211
|
-
when DMark::ElementNode
|
212
|
-
out << "<#{node.name}>"
|
213
|
-
handle_children(node)
|
214
|
-
out << "</#{node.name}>"
|
215
|
-
end
|
216
|
-
end
|
217
|
-
end
|
218
|
-
|
219
|
-
result = MyXMLLikeTranslator.new(nodes).run
|
220
|
-
puts result
|
221
|
-
----
|
data/lib/d-mark/cli.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
require_relative '../d-mark'
|
2
|
-
|
3
|
-
data = File.read(ARGV[0]).strip
|
4
|
-
|
5
|
-
parser = DMark::Parser.new(data)
|
6
|
-
begin
|
7
|
-
before = Time.now
|
8
|
-
result = parser.parse
|
9
|
-
after = Time.now
|
10
|
-
result.each do |tree|
|
11
|
-
puts tree.inspect
|
12
|
-
puts
|
13
|
-
end
|
14
|
-
puts "parse duration: #{(after - before).to_f}s"
|
15
|
-
rescue => e
|
16
|
-
case e
|
17
|
-
when DMark::Parser::ParserError
|
18
|
-
line = data.lines[e.line_nr]
|
19
|
-
|
20
|
-
puts "\e[31mError:\e[0m #{e.message}}"
|
21
|
-
puts
|
22
|
-
puts line
|
23
|
-
puts "\e[31m" + ' ' * e.col_nr + '↑' + "\e[0m"
|
24
|
-
exit 1
|
25
|
-
else
|
26
|
-
raise e
|
27
|
-
end
|
28
|
-
end
|