diml 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,13 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+
4
+ Style/StringLiterals:
5
+ Enabled: true
6
+ EnforcedStyle: double_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ Enabled: true
10
+ EnforcedStyle: double_quotes
11
+
12
+ Layout/LineLength:
13
+ Max: 120
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2022-07-05
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in diml.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "rubocop", "~> 1.21"
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
@@ -0,0 +1,17 @@
1
+ PROJECT=diml
2
+
3
+ console:
4
+ @bundle exec bin/console
5
+
6
+ setup:
7
+ @bundle exec bin/setup
8
+
9
+ install:
10
+ @bundle exec rake install
11
+
12
+ release:
13
+ @bundle exec rake release
14
+
15
+ release.gh:
16
+ @bundle exec rake release
17
+ @gh release create
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
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rubocop/rake_task"
5
+
6
+ RuboCop::RakeTask.new
7
+
8
+ task default: :rubocop
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`.
@@ -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])
@@ -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,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Content is a basic class that represents a non-keyword value
4
+ #
5
+ class Content
6
+ attr_reader :content
7
+
8
+ def initialize(value)
9
+ @content = value
10
+ end
11
+ 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,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./keyword"
4
+
5
+ class Heading < Keyword
6
+ extend Keyword::ClassMethods
7
+
8
+ def_token "heading."
9
+ 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,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./keyword"
4
+
5
+ class Point < Keyword
6
+ extend Keyword::ClassMethods
7
+
8
+ def_token "point."
9
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./keyword"
4
+
5
+ # Root is a special element that the DIML document does not specify
6
+ # It is used to start the content tree
7
+ class Root < Keyword
8
+ def virtual?
9
+ true
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./keyword"
4
+
5
+ class Section < Keyword
6
+ extend Keyword::ClassMethods
7
+
8
+ def_token "section."
9
+ 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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Diml
4
+ VERSION = "0.1.0"
5
+ end
data/lib/diml.rb ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "diml/version"
4
+ require_relative "diml/document/document"
5
+ require_relative "diml/format/formatter"
data/sig/diml.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Diml
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
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: []