diml 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rubocop.yml +13 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +48 -0
- data/LICENSE.txt +21 -0
- data/Makefile +17 -0
- data/README.md +99 -0
- data/Rakefile +8 -0
- data/diml.gemspec +38 -0
- data/docs/specification.md +46 -0
- data/examples/easy.diml +1 -0
- data/examples/heading.diml +1 -0
- data/examples/multiple_headings.diml +1 -0
- data/examples/parse_md.rb +38 -0
- data/examples/point.diml +1 -0
- data/examples/section.diml +1 -0
- data/exe/diml +0 -0
- data/lib/diml/document/content_tree.rb +39 -0
- data/lib/diml/document/document.rb +22 -0
- data/lib/diml/document/parser.rb +103 -0
- data/lib/diml/elements/content.rb +11 -0
- data/lib/diml/elements/factory.rb +17 -0
- data/lib/diml/elements/heading.rb +9 -0
- data/lib/diml/elements/keyword.rb +34 -0
- data/lib/diml/elements/keywords.rb +47 -0
- data/lib/diml/elements/point.rb +9 -0
- data/lib/diml/elements/root.rb +11 -0
- data/lib/diml/elements/section.rb +9 -0
- data/lib/diml/format/formatter.rb +84 -0
- data/lib/diml/format/markdown.rb +30 -0
- data/lib/diml/version.rb +5 -0
- data/lib/diml.rb +5 -0
- data/sig/diml.rbs +4 -0
- metadata +94 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3e688225588d8e5239464efa0873e93b07be46ac97dcfece27d93f9754a0210f
|
4
|
+
data.tar.gz: 5f7fd51e1eac09ee9b35178733cf61ffd20a3c2419a08780ae8e312ce18dcbca
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2de69226a26a89556b7e31080fe661e17b969605fca5cea38f11a58157b471f6bba1c2e5900b1d7fd1533600ca656c61cfc600a356daf39d536ac093c1ced669
|
7
|
+
data.tar.gz: 0432da9d0f971c2d4f8216a7520e202956ec88e9ec6df2fe6cab8f40e40e2a57511c486ab42e4b8dfe7926a7106346666e3cdb5bc8aebb208852e4537dc004d3
|
data/.rubocop.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
diml (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
ast (2.4.2)
|
10
|
+
coderay (1.1.3)
|
11
|
+
json (2.6.2)
|
12
|
+
method_source (1.0.0)
|
13
|
+
parallel (1.22.1)
|
14
|
+
parser (3.1.2.0)
|
15
|
+
ast (~> 2.4.1)
|
16
|
+
pry (0.14.1)
|
17
|
+
coderay (~> 1.1)
|
18
|
+
method_source (~> 1.0)
|
19
|
+
rainbow (3.1.1)
|
20
|
+
rake (13.0.6)
|
21
|
+
regexp_parser (2.5.0)
|
22
|
+
rexml (3.2.5)
|
23
|
+
rubocop (1.31.2)
|
24
|
+
json (~> 2.3)
|
25
|
+
parallel (~> 1.10)
|
26
|
+
parser (>= 3.1.0.0)
|
27
|
+
rainbow (>= 2.2.2, < 4.0)
|
28
|
+
regexp_parser (>= 1.8, < 3.0)
|
29
|
+
rexml (>= 3.2.5, < 4.0)
|
30
|
+
rubocop-ast (>= 1.18.0, < 2.0)
|
31
|
+
ruby-progressbar (~> 1.7)
|
32
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
33
|
+
rubocop-ast (1.18.0)
|
34
|
+
parser (>= 3.1.1.0)
|
35
|
+
ruby-progressbar (1.11.0)
|
36
|
+
unicode-display_width (2.2.0)
|
37
|
+
|
38
|
+
PLATFORMS
|
39
|
+
arm64-darwin-21
|
40
|
+
|
41
|
+
DEPENDENCIES
|
42
|
+
diml!
|
43
|
+
pry
|
44
|
+
rake (~> 13.0)
|
45
|
+
rubocop (~> 1.21)
|
46
|
+
|
47
|
+
BUNDLED WITH
|
48
|
+
2.3.7
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2022 aquaflamingo
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/Makefile
ADDED
data/README.md
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# Diml
|
2
|
+
|
3
|
+
Prototype [Dictation Markup Language](https://github.com/aquaflamingo/random-ideas/issues/6)
|
4
|
+
|
5
|
+
## Specification
|
6
|
+
A `diml` document provides a simple markup format for dictating notes in a way to preserve formatting for transformation to a presentable format (e.g. markdown). An example of DiML is below:
|
7
|
+
|
8
|
+
```
|
9
|
+
section. School Diary; heading. My First Day At School; point. I went to the lunch room; point. I went to the class room; point. I went to the nurse's room; heading. My Second Day At School; point. I went to the drama room; point. I went to the clown room;
|
10
|
+
```
|
11
|
+
|
12
|
+
Transcribes to:
|
13
|
+
```markdown
|
14
|
+
# School Diary
|
15
|
+
## My First Day At School
|
16
|
+
* I went to the lunch room
|
17
|
+
* I went to the class room
|
18
|
+
* I went to the nurse's room
|
19
|
+
## My Second Day At School
|
20
|
+
* I went to the drama room
|
21
|
+
* I went to the clown room
|
22
|
+
```
|
23
|
+
|
24
|
+
As you can see the above document isn't formatted but has the necessary keyword to parse the content and organize it into the format desired. This is helpful in the case of transcription of hand written notes via dictation.
|
25
|
+
|
26
|
+
See originating idea here: https://github.com/aquaflamingo/random-ideas/issues/6
|
27
|
+
|
28
|
+
## DiML document
|
29
|
+
A DiML document is a Tree data structure composed of _n_ children with at most height of 3 organized in a hierarchy: Section > Heading > Point.
|
30
|
+
|
31
|
+
Each node in the tree must be one of these elements described below.
|
32
|
+
|
33
|
+
Example of a DiML document:
|
34
|
+
```
|
35
|
+
root/
|
36
|
+
├─ section/
|
37
|
+
│ ├─ heading/
|
38
|
+
│ │ ├─ point
|
39
|
+
│ ├─ heading 2/
|
40
|
+
│ │ ├─ point
|
41
|
+
├─ section 2/
|
42
|
+
```
|
43
|
+
|
44
|
+
## Basic Elements
|
45
|
+
There are three basic elements to DiML:
|
46
|
+
|
47
|
+
1. Section: a content element for separating independent blocks of content in notes
|
48
|
+
2. Heading: a content element for separating themes for notes
|
49
|
+
3. Point: a content element for capturing a point in note notes
|
50
|
+
|
51
|
+
### Sections
|
52
|
+
An individual `.diml` document can have 1 to _n_ number of `Section`. `Section` cannot be children of other `Sections`. In other words, a `Section` can only be a siblings.
|
53
|
+
|
54
|
+
A `Section` can have 1 to _n_ child `Heading` elements.
|
55
|
+
|
56
|
+
### Headings
|
57
|
+
An individual `Heading` document can have 1 to _n_ number of `Point` elements. `Heading` cannot be children of other `Heading` elements. In other words, a `Heading` elements can only be a siblings.
|
58
|
+
|
59
|
+
A `Heading` can have 1 to _n_ child `Point` elements.
|
60
|
+
|
61
|
+
### Points
|
62
|
+
A `Point` can only be a child under a `Heading`.
|
63
|
+
|
64
|
+
## Installation
|
65
|
+
|
66
|
+
Add this line to your application's Gemfile:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
gem 'diml'
|
70
|
+
```
|
71
|
+
|
72
|
+
And then execute:
|
73
|
+
|
74
|
+
$ bundle install
|
75
|
+
|
76
|
+
Or install it yourself as:
|
77
|
+
|
78
|
+
$ gem install diml
|
79
|
+
|
80
|
+
## Usage
|
81
|
+
Assuming you have a `.diml` file according to the specification, you can copy the example of parsing `diml` to `markdown` in the example file:
|
82
|
+
|
83
|
+
```bash
|
84
|
+
ruby examples/parse_md.rb -f examples/easy.diml
|
85
|
+
```
|
86
|
+
|
87
|
+
## Development
|
88
|
+
|
89
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
90
|
+
|
91
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
92
|
+
|
93
|
+
## Contributing
|
94
|
+
|
95
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/diml.
|
96
|
+
|
97
|
+
## License
|
98
|
+
|
99
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/diml.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/diml/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "diml"
|
7
|
+
spec.version = Diml::VERSION
|
8
|
+
spec.authors = ["aquaflamingo"]
|
9
|
+
spec.email = ["16901597+aquaflamingo@users.noreply.github.com"]
|
10
|
+
|
11
|
+
spec.summary = "Sum"
|
12
|
+
spec.description = "Desc"
|
13
|
+
spec.homepage = "https://github.com/aquaflamingo/diml"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = ">= 2.6.0"
|
16
|
+
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
18
|
+
spec.metadata["source_code_uri"] = "https://github.com/aquaflamingo/diml"
|
19
|
+
spec.metadata["changelog_uri"] = "https://github.com/aquaflamingo/diml/releases"
|
20
|
+
|
21
|
+
# Specify which files should be added to the gem when it is released.
|
22
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
23
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
24
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
25
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
26
|
+
end
|
27
|
+
end
|
28
|
+
spec.bindir = "exe"
|
29
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
30
|
+
spec.require_paths = ["lib"]
|
31
|
+
|
32
|
+
# Uncomment to register a new dependency of your gem
|
33
|
+
# spec.add_dependency "example-gem", "~> 1.0"
|
34
|
+
spec.add_development_dependency "pry"
|
35
|
+
|
36
|
+
# For more information and examples about making a new gem, check out our
|
37
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
38
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
## Specification
|
2
|
+
A `diml` document provides a simple markup format for dictating notes in a way to preserve formatting for transformation to a presentable format (e.g. markdown). An example of DiML is below:
|
3
|
+
|
4
|
+
```
|
5
|
+
section. School Diary; heading. My First Day At School; point. I went to the lunch room; point. I went to the class room; point. I went to the nurse's room; heading. My Second Day At School; point. I went to the drama room; point. I went to the clown room;
|
6
|
+
```
|
7
|
+
|
8
|
+
As you can see the above document isn't formatted but has the necessary keyword to parse the content and organize it into the format desired. This is helpful in the case of transcription of hand written notes via dictation.
|
9
|
+
|
10
|
+
See originating idea here: https://github.com/aquaflamingo/random-ideas/issues/6
|
11
|
+
|
12
|
+
## DiML document
|
13
|
+
A DiML document is a Tree data structure composed of _n_ children with at most height of 3 organized in a hierarchy: Section > Heading > Point.
|
14
|
+
|
15
|
+
Each node in the tree must be one of these elements described below.
|
16
|
+
|
17
|
+
Example of a DiML document:
|
18
|
+
```
|
19
|
+
root/
|
20
|
+
├─ section/
|
21
|
+
│ ├─ heading/
|
22
|
+
│ │ ├─ point
|
23
|
+
│ ├─ heading 2/
|
24
|
+
│ │ ├─ point
|
25
|
+
├─ section 2/
|
26
|
+
```
|
27
|
+
|
28
|
+
## Basic Elements
|
29
|
+
There are three basic elements to DiML:
|
30
|
+
|
31
|
+
1. Section: a content element for separating independent blocks of content in notes
|
32
|
+
2. Heading: a content element for separating themes for notes
|
33
|
+
3. Point: a content element for capturing a point in note notes
|
34
|
+
|
35
|
+
### Sections
|
36
|
+
An individual `.diml` document can have 1 to _n_ number of `Section`. `Section` cannot be children of other `Sections`. In other words, a `Section` can only be a siblings.
|
37
|
+
|
38
|
+
A `Section` can have 1 to _n_ child `Heading` elements.
|
39
|
+
|
40
|
+
### Headings
|
41
|
+
An individual `Heading` document can have 1 to _n_ number of `Point` elements. `Heading` cannot be children of other `Heading` elements. In other words, a `Heading` elements can only be a siblings.
|
42
|
+
|
43
|
+
A `Heading` can have 1 to _n_ child `Point` elements.
|
44
|
+
|
45
|
+
### Points
|
46
|
+
A `Point` can only be a child under a `Heading`.
|
data/examples/easy.diml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
section. School Diary; heading. My First Day At School; point. I went to the lunch room; point. I went to the class room; point. I went to the nurse's room; heading. My Second Day At School; point. I went to the drama room; point. I went to the clown room;
|
@@ -0,0 +1 @@
|
|
1
|
+
heading. This is a heading
|
@@ -0,0 +1 @@
|
|
1
|
+
section.; heading. My First Day At School; point. I went to the lunch room; point. I went to the class room; point. I went to the nurse's room;
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "optionparser"
|
4
|
+
require_relative "../lib/diml"
|
5
|
+
|
6
|
+
opts = {}
|
7
|
+
|
8
|
+
# Input file in .diml format
|
9
|
+
# TODO could be file or input string
|
10
|
+
opts[:input] = ""
|
11
|
+
|
12
|
+
OptionParser.new do |parser|
|
13
|
+
parser.banner = "Dictation Markup Language (DiML)"
|
14
|
+
|
15
|
+
parser.on "-f VALUE", "-file VALUE", "Input file for parsing" do |f|
|
16
|
+
opts[:input] = f
|
17
|
+
end
|
18
|
+
|
19
|
+
parser.on "-v", "--version", "Show version" do
|
20
|
+
puts Diml::VERSION.to_s
|
21
|
+
exit
|
22
|
+
end
|
23
|
+
|
24
|
+
parser.on "-h", "--help", "Show help" do
|
25
|
+
puts parser
|
26
|
+
exit
|
27
|
+
end
|
28
|
+
end.parse!
|
29
|
+
|
30
|
+
def diml_to_md(input)
|
31
|
+
doc = Diml::Document.load(input)
|
32
|
+
|
33
|
+
formatter = Diml::Formatter.new(doc, :markdown)
|
34
|
+
|
35
|
+
formatter.format.string
|
36
|
+
end
|
37
|
+
|
38
|
+
puts diml_to_md(opts[:input])
|
data/examples/point.diml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
point. This is a point
|
@@ -0,0 +1 @@
|
|
1
|
+
section. This is a section;
|
data/exe/diml
ADDED
File without changes
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../elements/root"
|
4
|
+
|
5
|
+
#
|
6
|
+
# Basic multi child Tree datastructure
|
7
|
+
#
|
8
|
+
class ContentTree
|
9
|
+
attr_accessor :content, :parent
|
10
|
+
# ContentTree, Array<ContentTree>
|
11
|
+
attr_reader :children
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@children = []
|
15
|
+
@parent = nil
|
16
|
+
@content = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_child(child)
|
20
|
+
child.parent = self
|
21
|
+
@children << child
|
22
|
+
end
|
23
|
+
|
24
|
+
def root!
|
25
|
+
@parent = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def root?
|
29
|
+
@parent.nil?
|
30
|
+
end
|
31
|
+
|
32
|
+
def has_children?
|
33
|
+
@children.size.positive?
|
34
|
+
end
|
35
|
+
|
36
|
+
def leaf?
|
37
|
+
!has_children?
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Document is a tree
|
4
|
+
# Each tree node is an element
|
5
|
+
require_relative "./parser"
|
6
|
+
require_relative "./content_tree"
|
7
|
+
|
8
|
+
module Diml
|
9
|
+
class Document
|
10
|
+
attr_reader :content
|
11
|
+
|
12
|
+
def initialize(content_tree)
|
13
|
+
@content = content_tree
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.load(path)
|
17
|
+
c = File.read(path)
|
18
|
+
|
19
|
+
new(Parser.new(c).parse)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../elements/factory.rb"
|
4
|
+
require_relative "../elements/section"
|
5
|
+
require_relative "../elements/point"
|
6
|
+
require_relative "../elements/heading"
|
7
|
+
|
8
|
+
# Basic document parser
|
9
|
+
module Diml
|
10
|
+
class Parser
|
11
|
+
def initialize(raw_content)
|
12
|
+
@raw_content = raw_content
|
13
|
+
end
|
14
|
+
|
15
|
+
# Parses the raw content for a DIML document into
|
16
|
+
# the internal representation.
|
17
|
+
def parse
|
18
|
+
basic_tokens = @raw_content.split(";")
|
19
|
+
|
20
|
+
# Create new Content Tree
|
21
|
+
ctree = ContentTree.new
|
22
|
+
ctree.content = Root.new("")
|
23
|
+
ctree.root!
|
24
|
+
|
25
|
+
tree = build_tree(ctree, basic_tokens)
|
26
|
+
|
27
|
+
# Return the root not the last value
|
28
|
+
t = tree
|
29
|
+
t = t.parent until t.root?
|
30
|
+
t
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# Recursive content tree builder
|
36
|
+
def build_tree(ctree, tokens)
|
37
|
+
# Basic case: if there are no nestable tokens
|
38
|
+
return ctree if tokens.empty?
|
39
|
+
|
40
|
+
# Pull first element from the start of the array
|
41
|
+
next_token = tokens.first.strip
|
42
|
+
tokens = tokens.drop(1)
|
43
|
+
|
44
|
+
# Create a new element
|
45
|
+
element = Factory.new_element(next_token)
|
46
|
+
|
47
|
+
case element.class.name
|
48
|
+
when Section.name
|
49
|
+
if ctree.root?
|
50
|
+
# Sections can only belong as children to the root.
|
51
|
+
child = add_to_tree(ctree, element)
|
52
|
+
else
|
53
|
+
# Continue to traverse upwards until you find the root
|
54
|
+
ancestor = ctree.parent
|
55
|
+
ancestor = ancestor.parent until ancestor.root?
|
56
|
+
|
57
|
+
# Ancestor is root. Insert this section.
|
58
|
+
child = add_to_tree(ancestor, element)
|
59
|
+
end
|
60
|
+
|
61
|
+
build_tree(child, tokens)
|
62
|
+
when Heading.name
|
63
|
+
if ctree.content.instance_of? Section
|
64
|
+
# Headings can only belong to sections
|
65
|
+
child = add_to_tree(ctree, element)
|
66
|
+
else
|
67
|
+
ancestor = ctree.parent
|
68
|
+
ancestor = ancestor.parent until ancestor.content.instance_of? Section
|
69
|
+
|
70
|
+
# Ancestor is root. Insert this section.
|
71
|
+
child = add_to_tree(ancestor, element)
|
72
|
+
end
|
73
|
+
|
74
|
+
build_tree(child, tokens)
|
75
|
+
when Point.name
|
76
|
+
if ctree.content.instance_of? Heading
|
77
|
+
# Headings can only belong to sections
|
78
|
+
child = add_to_tree(ctree, element)
|
79
|
+
else
|
80
|
+
ancestor = ctree.parent
|
81
|
+
ancestor = ancestor.parent until ancestor.content.instance_of? Heading
|
82
|
+
|
83
|
+
# Ancestor is root. Insert this section.
|
84
|
+
child = add_to_tree(ancestor, element)
|
85
|
+
end
|
86
|
+
|
87
|
+
build_tree(child, tokens)
|
88
|
+
end
|
89
|
+
|
90
|
+
ctree
|
91
|
+
end
|
92
|
+
|
93
|
+
# Helper method to create and add a new element to a content tree
|
94
|
+
def add_to_tree(tree, child_element)
|
95
|
+
child = ContentTree.new
|
96
|
+
child.parent = tree
|
97
|
+
child.content = child_element
|
98
|
+
tree.add_child(child)
|
99
|
+
|
100
|
+
child
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "./content"
|
4
|
+
require_relative "./keywords"
|
5
|
+
|
6
|
+
# Element is a concrete implementation of Element
|
7
|
+
# It is used in conjunction with a ContentTree and is renderable
|
8
|
+
class Factory
|
9
|
+
# Factory method for creating the appropriate class under the hood via the token
|
10
|
+
def self.new_element(content)
|
11
|
+
if Keywords.is_kw?(content)
|
12
|
+
Keywords.build_from(content)
|
13
|
+
else
|
14
|
+
Content.new(content)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# A Keyword is a special word in the lexicon of DiML.
|
4
|
+
# A Keyword is distinct from raw content
|
5
|
+
# Example: "section"
|
6
|
+
require "pry"
|
7
|
+
|
8
|
+
class Keyword
|
9
|
+
attr_reader :content
|
10
|
+
|
11
|
+
def initialize(content)
|
12
|
+
@content = content
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns whether or not the keyword is intended to be a virtual or concrete element
|
16
|
+
# By default keywords should be non-virtual
|
17
|
+
def virtual?
|
18
|
+
false
|
19
|
+
end
|
20
|
+
|
21
|
+
module ClassMethods
|
22
|
+
def def_token(value)
|
23
|
+
define_singleton_method("token") { value }
|
24
|
+
end
|
25
|
+
|
26
|
+
def try(v)
|
27
|
+
# TODO: add more intelligence to this
|
28
|
+
# i.e. not just starting with
|
29
|
+
return false if v.nil?
|
30
|
+
|
31
|
+
v.start_with?(token)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "./heading"
|
4
|
+
require_relative "./section"
|
5
|
+
require_relative "./point"
|
6
|
+
|
7
|
+
module Keywords
|
8
|
+
ALL = [
|
9
|
+
Heading,
|
10
|
+
Section,
|
11
|
+
Point
|
12
|
+
].freeze
|
13
|
+
|
14
|
+
#
|
15
|
+
# Factory method to create a keyword from raw content
|
16
|
+
#
|
17
|
+
def self.build_from(content)
|
18
|
+
found_klasses = ALL.select do |kw|
|
19
|
+
kw.try(content)
|
20
|
+
end
|
21
|
+
|
22
|
+
raise ArgumentError, "Provided content is not a keyword" if found_klasses.empty?
|
23
|
+
|
24
|
+
klass = found_klasses.first
|
25
|
+
|
26
|
+
# Split the content into two parts the keyword, and content.
|
27
|
+
# @Example: Q. headingvalue -> [Q., headingvalue]
|
28
|
+
_, remaining = content.split(klass.token)
|
29
|
+
|
30
|
+
stripped = if remaining.nil? || remaining.empty?
|
31
|
+
""
|
32
|
+
else
|
33
|
+
remaining.strip
|
34
|
+
end
|
35
|
+
# Instantiate the keyword class with the content
|
36
|
+
klass.new(stripped)
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# Returns whether or not the supplied value is a keyword
|
41
|
+
#
|
42
|
+
def self.is_kw?(v)
|
43
|
+
!ALL.select do |kw|
|
44
|
+
kw.try(v)
|
45
|
+
end.empty?
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "stringio"
|
4
|
+
|
5
|
+
require_relative "./markdown"
|
6
|
+
|
7
|
+
module Diml
|
8
|
+
# Main Formatting class responsible for traversing the document content
|
9
|
+
# tree and rendering according to the strategy
|
10
|
+
class Formatter
|
11
|
+
# Decorator class for basic content
|
12
|
+
# Receives an keyword in the DiML language and
|
13
|
+
# renders it within the Markdown format.
|
14
|
+
|
15
|
+
def initialize(diml_document, strategy = :markdown)
|
16
|
+
@doc = diml_document
|
17
|
+
@decorator = select_decorator(strategy)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Creates a plain string of markdown
|
21
|
+
# For use by client to save as a file
|
22
|
+
# if chosen.
|
23
|
+
def format
|
24
|
+
content_tree = @doc.content
|
25
|
+
|
26
|
+
# If the root has no children just render an empty string
|
27
|
+
raise ArgumentError, "Document is invalid: no root found for content tree" unless content_tree.root?
|
28
|
+
|
29
|
+
raise ArgumentError, "Document is invalid: no content found" unless content_tree.has_children?
|
30
|
+
|
31
|
+
io = StringIO.new
|
32
|
+
recursive_render(content_tree, io)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def recursive_render(tree, doc_builder)
|
38
|
+
# Recursive Render
|
39
|
+
# Start at root element
|
40
|
+
#
|
41
|
+
# Base case:
|
42
|
+
# if there are no children
|
43
|
+
# return the node content decorated
|
44
|
+
# Otherwise:
|
45
|
+
# For each child in tree
|
46
|
+
# Recursive render
|
47
|
+
|
48
|
+
if tree.leaf?
|
49
|
+
# Do not render virtual elements
|
50
|
+
db = write_to_document(doc_builder, tree.content)
|
51
|
+
|
52
|
+
return db
|
53
|
+
end
|
54
|
+
|
55
|
+
db = write_to_document(doc_builder, tree.content)
|
56
|
+
|
57
|
+
tree.children.each do |c|
|
58
|
+
db = recursive_render(c, db)
|
59
|
+
end
|
60
|
+
|
61
|
+
db
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Write the content element to the document after decorating it
|
66
|
+
#
|
67
|
+
# Currently only supports Markdown format
|
68
|
+
#
|
69
|
+
def write_to_document(builder, element)
|
70
|
+
builder.puts(@decorator.render(element)) unless element.virtual?
|
71
|
+
|
72
|
+
builder
|
73
|
+
end
|
74
|
+
|
75
|
+
def select_decorator(strategy)
|
76
|
+
case strategy
|
77
|
+
when :markdown
|
78
|
+
Format::Markdown.new
|
79
|
+
else
|
80
|
+
raise ArgumentError, "Invalid decoration format: #{strategy}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../elements/point"
|
4
|
+
require_relative "../elements/heading"
|
5
|
+
require_relative "../elements/section"
|
6
|
+
require_relative "../elements/root"
|
7
|
+
require_relative "../elements/content"
|
8
|
+
|
9
|
+
module Format
|
10
|
+
class Markdown
|
11
|
+
# Maintains a map to conserve the translation values
|
12
|
+
# of DIML lexicon to markdown prefixes.
|
13
|
+
FORMAT_PREFIXES = {
|
14
|
+
Point => "*",
|
15
|
+
Heading => "##",
|
16
|
+
Section => "#",
|
17
|
+
Content => "",
|
18
|
+
Root => ""
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
# Returns the element with appropriate
|
22
|
+
def render(elem)
|
23
|
+
prefix = FORMAT_PREFIXES[elem.class]
|
24
|
+
|
25
|
+
raise ArgumentError, "Invalid element: #{elem.class}" if prefix.nil?
|
26
|
+
|
27
|
+
"#{prefix} #{elem.content}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/diml/version.rb
ADDED
data/lib/diml.rb
ADDED
data/sig/diml.rbs
ADDED
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: diml
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- aquaflamingo
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-09-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pry
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: Desc
|
28
|
+
email:
|
29
|
+
- 16901597+aquaflamingo@users.noreply.github.com
|
30
|
+
executables:
|
31
|
+
- diml
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- ".rubocop.yml"
|
36
|
+
- CHANGELOG.md
|
37
|
+
- Gemfile
|
38
|
+
- Gemfile.lock
|
39
|
+
- LICENSE.txt
|
40
|
+
- Makefile
|
41
|
+
- README.md
|
42
|
+
- Rakefile
|
43
|
+
- diml.gemspec
|
44
|
+
- docs/specification.md
|
45
|
+
- examples/easy.diml
|
46
|
+
- examples/heading.diml
|
47
|
+
- examples/multiple_headings.diml
|
48
|
+
- examples/parse_md.rb
|
49
|
+
- examples/point.diml
|
50
|
+
- examples/section.diml
|
51
|
+
- exe/diml
|
52
|
+
- lib/diml.rb
|
53
|
+
- lib/diml/document/content_tree.rb
|
54
|
+
- lib/diml/document/document.rb
|
55
|
+
- lib/diml/document/parser.rb
|
56
|
+
- lib/diml/elements/content.rb
|
57
|
+
- lib/diml/elements/factory.rb
|
58
|
+
- lib/diml/elements/heading.rb
|
59
|
+
- lib/diml/elements/keyword.rb
|
60
|
+
- lib/diml/elements/keywords.rb
|
61
|
+
- lib/diml/elements/point.rb
|
62
|
+
- lib/diml/elements/root.rb
|
63
|
+
- lib/diml/elements/section.rb
|
64
|
+
- lib/diml/format/formatter.rb
|
65
|
+
- lib/diml/format/markdown.rb
|
66
|
+
- lib/diml/version.rb
|
67
|
+
- sig/diml.rbs
|
68
|
+
homepage: https://github.com/aquaflamingo/diml
|
69
|
+
licenses:
|
70
|
+
- MIT
|
71
|
+
metadata:
|
72
|
+
homepage_uri: https://github.com/aquaflamingo/diml
|
73
|
+
source_code_uri: https://github.com/aquaflamingo/diml
|
74
|
+
changelog_uri: https://github.com/aquaflamingo/diml/releases
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 2.6.0
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
requirements: []
|
90
|
+
rubygems_version: 3.3.7
|
91
|
+
signing_key:
|
92
|
+
specification_version: 4
|
93
|
+
summary: Sum
|
94
|
+
test_files: []
|