rosetta-ruby 0.0.0
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 +7 -0
- data/.DS_Store +0 -0
- data/.gitignore +58 -0
- data/.rubocop.yml +8 -0
- data/.vscode/settings.json +3 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +16 -0
- data/README.md +48 -0
- data/TODOS.md +11 -0
- data/lib/rosetta/abstract_syntax_tree.rb +28 -0
- data/lib/rosetta/formatters/html/html_generator.rb +96 -0
- data/lib/rosetta/formatters/html/html_writer.rb +19 -0
- data/lib/rosetta/service_base.rb +8 -0
- data/lib/rosetta/services/inline_token_resolver.rb +80 -0
- data/lib/rosetta/services/input_scanner.rb +15 -0
- data/lib/rosetta/services/token_resolver.rb +42 -0
- data/lib/rosetta/services/tree_parser.rb +126 -0
- data/lib/rosetta/tokens/basic_list.rb +10 -0
- data/lib/rosetta/tokens/basic_list_item.rb +19 -0
- data/lib/rosetta/tokens/basic_token.rb +51 -0
- data/lib/rosetta/tokens/block_quote.rb +10 -0
- data/lib/rosetta/tokens/block_token.rb +36 -0
- data/lib/rosetta/tokens/bold.rb +21 -0
- data/lib/rosetta/tokens/break.rb +20 -0
- data/lib/rosetta/tokens/code_block.rb +10 -0
- data/lib/rosetta/tokens/code_block_delimiter.rb +18 -0
- data/lib/rosetta/tokens/header.rb +42 -0
- data/lib/rosetta/tokens/inline_code.rb +25 -0
- data/lib/rosetta/tokens/italics.rb +21 -0
- data/lib/rosetta/tokens/line_break.rb +18 -0
- data/lib/rosetta/tokens/link.rb +66 -0
- data/lib/rosetta/tokens/new_line.rb +20 -0
- data/lib/rosetta/tokens/numbered_list.rb +10 -0
- data/lib/rosetta/tokens/numbered_list_item.rb +25 -0
- data/lib/rosetta/tokens/paragraph.rb +10 -0
- data/lib/rosetta/tokens/quote.rb +24 -0
- data/lib/rosetta/tokens/shared/inline_tokens.rb +30 -0
- data/lib/rosetta/tokens/strikethrough.rb +29 -0
- data/lib/rosetta/tokens/text.rb +14 -0
- data/lib/rosetta-ruby.rb +15 -0
- data/main.rb +21 -0
- data/rosetta-ruby.gemspec +16 -0
- data/samples/all.md +50 -0
- data/samples/bold.md +1 -0
- data/samples/code_blocks.md +5 -0
- data/samples/headers.md +11 -0
- data/samples/link.md +3 -0
- data/samples/lists.md +10 -0
- data/samples/quotes.md +6 -0
- data/samples/text.md +5 -0
- metadata +91 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require './lib/rosetta/tokens/basic_token'
|
4
|
+
|
5
|
+
# Handles logic for Link tokens
|
6
|
+
class Link < BasicToken
|
7
|
+
def self.matches?(text)
|
8
|
+
# Naive check for link.
|
9
|
+
return false unless text.start_with?('[')
|
10
|
+
|
11
|
+
closing_bracket_index = text.index(']')
|
12
|
+
return false if closing_bracket_index.nil?
|
13
|
+
|
14
|
+
return false if text[closing_bracket_index + 1] != '('
|
15
|
+
|
16
|
+
text[closing_bracket_index + 1..].include?(')')
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.consume(text)
|
20
|
+
closing_index = text.rindex(')')
|
21
|
+
source = text[0..closing_index]
|
22
|
+
|
23
|
+
new(source)
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
"<Token type='#{type}' value='#{value}' url='#{url}'>"
|
28
|
+
end
|
29
|
+
|
30
|
+
def node_representation
|
31
|
+
"<#{type} value='#{value}' url='#{url}'>"
|
32
|
+
end
|
33
|
+
|
34
|
+
def type
|
35
|
+
:LINK
|
36
|
+
end
|
37
|
+
|
38
|
+
def value
|
39
|
+
final_bracket_index = url_beginning_index - 1
|
40
|
+
|
41
|
+
# We want the value inside brackets, and should exclude the brackets themselves.
|
42
|
+
@source_text[1...final_bracket_index]
|
43
|
+
end
|
44
|
+
|
45
|
+
def url
|
46
|
+
# We want the value inside the parenthesis, and should exclude the the parens themselves.
|
47
|
+
@source_text[url_beginning_index + 1...-1]
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def url_beginning_index
|
53
|
+
open_bracket_count = 0
|
54
|
+
|
55
|
+
@url_beginning_index ||= @source_text.each_char.with_index do |character, index|
|
56
|
+
case character
|
57
|
+
when '['
|
58
|
+
open_bracket_count += 1
|
59
|
+
when ']'
|
60
|
+
open_bracket_count -= 1
|
61
|
+
end
|
62
|
+
|
63
|
+
return index + 1 if open_bracket_count.zero?
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require './lib/rosetta/tokens/basic_token'
|
4
|
+
|
5
|
+
# Handles logic for New Line tokens
|
6
|
+
class NewLine < BasicToken
|
7
|
+
def self.matches?(text)
|
8
|
+
text == ''
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize; end
|
12
|
+
|
13
|
+
def type
|
14
|
+
:NEW_LINE
|
15
|
+
end
|
16
|
+
|
17
|
+
def value
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require './lib/rosetta/tokens/basic_token'
|
4
|
+
|
5
|
+
# Handles logic for Header tokens
|
6
|
+
class NumberedListItem < BasicToken
|
7
|
+
def self.matches?(text)
|
8
|
+
return false unless number?(text[0])
|
9
|
+
|
10
|
+
text[1..2] == '. '
|
11
|
+
end
|
12
|
+
|
13
|
+
def type
|
14
|
+
:NUMBERED_LIST_ITEM
|
15
|
+
end
|
16
|
+
|
17
|
+
def value
|
18
|
+
# If the code reaches here we know the first 3 chars are the list delimiter.
|
19
|
+
@source_text[3..-1]
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.number?(object)
|
23
|
+
object.to_s == object.to_i.to_s
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require './lib/rosetta/tokens/basic_token'
|
4
|
+
|
5
|
+
# Handles logic for Header tokens
|
6
|
+
class Quote < BasicToken
|
7
|
+
def self.matches?(text)
|
8
|
+
text.start_with?('> ')
|
9
|
+
end
|
10
|
+
|
11
|
+
def type
|
12
|
+
:QUOTE
|
13
|
+
end
|
14
|
+
|
15
|
+
def value
|
16
|
+
@value ||= extract_value_from_text
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def extract_value_from_text
|
22
|
+
@source_text.delete_prefix('> ')
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shared
|
4
|
+
# Handles logic for class methods for Inline Tokens.
|
5
|
+
module InlineTokens
|
6
|
+
def delimiter_length
|
7
|
+
delimiter_token.length
|
8
|
+
end
|
9
|
+
|
10
|
+
def matches?(text)
|
11
|
+
text.start_with?(delimiter_token) && valid_delimiter?(text[delimiter_length..])
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid_delimiter?(text)
|
15
|
+
delimiter_index = text.index(delimiter_token)
|
16
|
+
!delimiter_index.nil?
|
17
|
+
end
|
18
|
+
|
19
|
+
def consume(text)
|
20
|
+
delimiter_index = text[delimiter_length..].index(delimiter_token) + delimiter_length
|
21
|
+
|
22
|
+
source = text[0..delimiter_index]
|
23
|
+
new(source)
|
24
|
+
end
|
25
|
+
|
26
|
+
def delimiter_token
|
27
|
+
const_get('DELIMITER_TOKEN')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require './lib/rosetta/tokens/basic_token'
|
4
|
+
require './lib/rosetta/tokens/shared/inline_tokens'
|
5
|
+
|
6
|
+
# Handles logic for Strikethrough tokens
|
7
|
+
class Strikethrough < BasicToken
|
8
|
+
extend Shared::InlineTokens
|
9
|
+
|
10
|
+
DELIMITER_TOKEN = '~~'
|
11
|
+
|
12
|
+
def self.consume(raw_text)
|
13
|
+
# Add an extra 1 to offset the extra delimiter length (because strikethrough).
|
14
|
+
delimiter_index = raw_text[delimiter_length..].index(DELIMITER_TOKEN) + delimiter_length + 1
|
15
|
+
|
16
|
+
source = raw_text[0..delimiter_index]
|
17
|
+
new(source)
|
18
|
+
end
|
19
|
+
|
20
|
+
def type
|
21
|
+
:STRIKETHROUGH
|
22
|
+
end
|
23
|
+
|
24
|
+
def value
|
25
|
+
return DELIMITER_TOKEN * 2 if @source_text == DELIMITER_TOKEN * 2
|
26
|
+
|
27
|
+
@source_text[self.class.delimiter_length...-self.class.delimiter_length]
|
28
|
+
end
|
29
|
+
end
|
data/lib/rosetta-ruby.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require './lib/rosetta/abstract_syntax_tree'
|
4
|
+
require './lib/rosetta/formatters/html/html_writer'
|
5
|
+
require './lib/rosetta/services/input_scanner'
|
6
|
+
|
7
|
+
# Entry point for rosetta-ruby gem.
|
8
|
+
class Rosetta
|
9
|
+
def self.markdown_to_html(markdown_source)
|
10
|
+
source_tokens = InputScanner.call(markdown_source)
|
11
|
+
abstract_syntax_tree = AbstractSyntaxTree.new(source_tokens)
|
12
|
+
|
13
|
+
HTMLWriter.call(abstract_syntax_tree.token_tree)
|
14
|
+
end
|
15
|
+
end
|
data/main.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require './lib/rosetta/abstract_syntax_tree'
|
4
|
+
require './lib/rosetta/formatters/html/html_writer'
|
5
|
+
require './lib/rosetta/services/input_scanner'
|
6
|
+
|
7
|
+
# We ignore the issue of large file sizes for now.
|
8
|
+
# TODO: Optimise file reading, i.e. stream input for large source files.
|
9
|
+
|
10
|
+
puts 'Correct usage: ruby main.rb <source_filename>' if ARGV.length != 1
|
11
|
+
|
12
|
+
source_filename = ARGV[0]
|
13
|
+
source_file = File.read(source_filename)
|
14
|
+
|
15
|
+
source_tokens = InputScanner.call(source_file)
|
16
|
+
|
17
|
+
abstract_syntax_tree = AbstractSyntaxTree.new(source_tokens)
|
18
|
+
|
19
|
+
html_output = HTMLWriter.call(abstract_syntax_tree.token_tree)
|
20
|
+
|
21
|
+
puts html_output
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = 'rosetta-ruby'
|
5
|
+
spec.version = '0.0.0'
|
6
|
+
spec.summary = 'Rosetta'
|
7
|
+
spec.description = 'Simple Markdown to HTML transformation.'
|
8
|
+
spec.authors = ['Matt Craig']
|
9
|
+
spec.email = 'mattcraig365@gmail.com'
|
10
|
+
spec.files = `git ls-files`.split("\n")
|
11
|
+
spec.homepage = 'https://rubygems.org/gems/rosetta-ruby'
|
12
|
+
spec.license = 'MIT'
|
13
|
+
spec.required_ruby_version = '>= 3.0.0'
|
14
|
+
|
15
|
+
# spec.add_development_dependency ['rspec', 'byebug']
|
16
|
+
end
|
data/samples/all.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# Testing all block features
|
2
|
+
|
3
|
+
## Code Blocks
|
4
|
+
|
5
|
+
```
|
6
|
+
def test
|
7
|
+
return true
|
8
|
+
end
|
9
|
+
```
|
10
|
+
|
11
|
+
This is *bold text*!
|
12
|
+
*But* should all be part of the same paragraph.
|
13
|
+
|
14
|
+
This is _italicized text_!
|
15
|
+
|
16
|
+
This is ~~struck through~~!
|
17
|
+
|
18
|
+
This is an [inline) link](https://mattcraig.me)!
|
19
|
+
|
20
|
+
This is `inline code`!
|
21
|
+
|
22
|
+
## Links
|
23
|
+
|
24
|
+
[A link.](https://example.com)
|
25
|
+
|
26
|
+
## Quotes
|
27
|
+
|
28
|
+
> I'd rather be eating tacos.
|
29
|
+
> And writing quotes, of course.
|
30
|
+
|
31
|
+
In the middle of everything, some text!
|
32
|
+
|
33
|
+
## Bullet List
|
34
|
+
|
35
|
+
- Thing 1
|
36
|
+
- Thing 2
|
37
|
+
- Thing 3
|
38
|
+
|
39
|
+
## Another Bullet List
|
40
|
+
|
41
|
+
- A simple list
|
42
|
+
- With two items
|
43
|
+
|
44
|
+
More text!
|
45
|
+
|
46
|
+
## Ordered List
|
47
|
+
|
48
|
+
1. Order one
|
49
|
+
2. Order two
|
50
|
+
3. Order three
|
data/samples/bold.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*This is* too.
|
data/samples/headers.md
ADDED
data/samples/link.md
ADDED
data/samples/lists.md
ADDED
data/samples/quotes.md
ADDED
data/samples/text.md
ADDED
metadata
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rosetta-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matt Craig
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-08-30 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Simple Markdown to HTML transformation.
|
14
|
+
email: mattcraig365@gmail.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- ".DS_Store"
|
20
|
+
- ".gitignore"
|
21
|
+
- ".rubocop.yml"
|
22
|
+
- ".vscode/settings.json"
|
23
|
+
- Gemfile
|
24
|
+
- Gemfile.lock
|
25
|
+
- README.md
|
26
|
+
- TODOS.md
|
27
|
+
- lib/rosetta-ruby.rb
|
28
|
+
- lib/rosetta/abstract_syntax_tree.rb
|
29
|
+
- lib/rosetta/formatters/html/html_generator.rb
|
30
|
+
- lib/rosetta/formatters/html/html_writer.rb
|
31
|
+
- lib/rosetta/service_base.rb
|
32
|
+
- lib/rosetta/services/inline_token_resolver.rb
|
33
|
+
- lib/rosetta/services/input_scanner.rb
|
34
|
+
- lib/rosetta/services/token_resolver.rb
|
35
|
+
- lib/rosetta/services/tree_parser.rb
|
36
|
+
- lib/rosetta/tokens/basic_list.rb
|
37
|
+
- lib/rosetta/tokens/basic_list_item.rb
|
38
|
+
- lib/rosetta/tokens/basic_token.rb
|
39
|
+
- lib/rosetta/tokens/block_quote.rb
|
40
|
+
- lib/rosetta/tokens/block_token.rb
|
41
|
+
- lib/rosetta/tokens/bold.rb
|
42
|
+
- lib/rosetta/tokens/break.rb
|
43
|
+
- lib/rosetta/tokens/code_block.rb
|
44
|
+
- lib/rosetta/tokens/code_block_delimiter.rb
|
45
|
+
- lib/rosetta/tokens/header.rb
|
46
|
+
- lib/rosetta/tokens/inline_code.rb
|
47
|
+
- lib/rosetta/tokens/italics.rb
|
48
|
+
- lib/rosetta/tokens/line_break.rb
|
49
|
+
- lib/rosetta/tokens/link.rb
|
50
|
+
- lib/rosetta/tokens/new_line.rb
|
51
|
+
- lib/rosetta/tokens/numbered_list.rb
|
52
|
+
- lib/rosetta/tokens/numbered_list_item.rb
|
53
|
+
- lib/rosetta/tokens/paragraph.rb
|
54
|
+
- lib/rosetta/tokens/quote.rb
|
55
|
+
- lib/rosetta/tokens/shared/inline_tokens.rb
|
56
|
+
- lib/rosetta/tokens/strikethrough.rb
|
57
|
+
- lib/rosetta/tokens/text.rb
|
58
|
+
- main.rb
|
59
|
+
- rosetta-ruby.gemspec
|
60
|
+
- samples/all.md
|
61
|
+
- samples/bold.md
|
62
|
+
- samples/code_blocks.md
|
63
|
+
- samples/headers.md
|
64
|
+
- samples/link.md
|
65
|
+
- samples/lists.md
|
66
|
+
- samples/quotes.md
|
67
|
+
- samples/text.md
|
68
|
+
homepage: https://rubygems.org/gems/rosetta-ruby
|
69
|
+
licenses:
|
70
|
+
- MIT
|
71
|
+
metadata: {}
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 3.0.0
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
requirements: []
|
87
|
+
rubygems_version: 3.2.3
|
88
|
+
signing_key:
|
89
|
+
specification_version: 4
|
90
|
+
summary: Rosetta
|
91
|
+
test_files: []
|