erle 0.1.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/.gitignore +79 -0
- data/.rspec +3 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +49 -0
- data/Rakefile +6 -0
- data/bin/console +12 -0
- data/bin/setup +8 -0
- data/erle.gemspec +34 -0
- data/lib/erle.rb +28 -0
- data/lib/erle/elements/atom.rb +44 -0
- data/lib/erle/elements/binary.rb +31 -0
- data/lib/erle/elements/enum.rb +69 -0
- data/lib/erle/elements/list.rb +9 -0
- data/lib/erle/elements/map.rb +9 -0
- data/lib/erle/elements/numbers.rb +26 -0
- data/lib/erle/elements/pid.rb +22 -0
- data/lib/erle/elements/ref.rb +37 -0
- data/lib/erle/elements/string.rb +33 -0
- data/lib/erle/elements/term.rb +71 -0
- data/lib/erle/elements/tuple.rb +37 -0
- data/lib/erle/errors.rb +12 -0
- data/lib/erle/parser.rb +122 -0
- data/lib/erle/registry.rb +71 -0
- data/lib/erle/version.rb +3 -0
- data/spec/erle/parser/atom_spec.rb +33 -0
- data/spec/erle/parser/binary_spec.rb +19 -0
- data/spec/erle/parser/enum_spec.rb +20 -0
- data/spec/erle/parser/numbers_spec.rb +21 -0
- data/spec/erle/parser/parser_spec.rb +135 -0
- data/spec/erle/parser/pid_spec.rb +18 -0
- data/spec/erle/parser/ref_spec.rb +20 -0
- data/spec/erle/parser/term_spec.rb +20 -0
- data/spec/erle/parser/tuple_spec.rb +27 -0
- data/spec/erle/parser_spec.rb +7 -0
- data/spec/spec_helper.rb +22 -0
- metadata +153 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
module ERLE
|
2
|
+
|
3
|
+
class Number < Term
|
4
|
+
|
5
|
+
end
|
6
|
+
|
7
|
+
class Float < Number
|
8
|
+
pattern %r{[-0-9]\.[0-9]+}
|
9
|
+
|
10
|
+
def to_ruby
|
11
|
+
@output ||= @input.to_f
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
class Integer < Number
|
17
|
+
|
18
|
+
pattern %r{(-?0|-?[1-9]\d*)}
|
19
|
+
|
20
|
+
def to_ruby
|
21
|
+
@output ||= @input.to_i
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ERLE
|
2
|
+
|
3
|
+
# http://erlang.org/doc/reference_manual/data_types.html#id68255
|
4
|
+
class Pid < Term
|
5
|
+
|
6
|
+
class Data < ::String
|
7
|
+
|
8
|
+
def to_erl
|
9
|
+
"<#{self}>"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
enclosure /</, />/
|
14
|
+
pattern %r{[-0-9](\.[0-9])+}
|
15
|
+
# @delimeter = '.'
|
16
|
+
|
17
|
+
def to_ruby
|
18
|
+
@output ||= Data.new(@input)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ERLE
|
2
|
+
|
3
|
+
class Ref < Term
|
4
|
+
enclosure /\#Ref</, />/
|
5
|
+
|
6
|
+
@delimeter = "."
|
7
|
+
|
8
|
+
@pattern = %r{(?:(\d+)\.?)+}
|
9
|
+
|
10
|
+
def initialize(input)
|
11
|
+
@input = input
|
12
|
+
end
|
13
|
+
|
14
|
+
# TODO: Leverage Enum, and refactor (Enum) to handle conflicting delimiters
|
15
|
+
# e.g. a "." delimeter is preempted by the "float" pattern.
|
16
|
+
def to_ruby
|
17
|
+
@output ||= @input.split(".")
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.parse(parser)
|
21
|
+
|
22
|
+
result = parser.scan(@pattern)
|
23
|
+
|
24
|
+
if close && !parser.scan(close)
|
25
|
+
raise parser.raise_unexpected_token("Expected term closure \" #{close.source} \"")
|
26
|
+
end
|
27
|
+
|
28
|
+
# Make sure we kill any trailing close
|
29
|
+
# TODO: Consider raising if no match?
|
30
|
+
# TODO: Consider doing only if we started with an opening...
|
31
|
+
# parser.scan(ERLE::Registry.closings_regex)
|
32
|
+
new(result)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module ERLE
|
2
|
+
|
3
|
+
class String < Term
|
4
|
+
enclosure /\"/
|
5
|
+
# pattern %r{((?:[^\x0-\x1f'\\] |
|
6
|
+
# # escaped special characters:
|
7
|
+
# \\["/bfnrt] |
|
8
|
+
# \\u[0-9a-fA-F]{4} |
|
9
|
+
# # match all but escaped special characters:
|
10
|
+
# \\[\x20-\x21\x23-\x2e\x30-\x5b\x5d-\x61\x63-\x65\x67-\x6d\x6f-\x71\x73\x75-\xff])*)
|
11
|
+
# }nx
|
12
|
+
|
13
|
+
PATTERN = %r{[^\"]*}nx
|
14
|
+
|
15
|
+
def to_ruby
|
16
|
+
# self.str.gsub(/"/,"")
|
17
|
+
@output ||= @input
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.parse(parser)
|
21
|
+
opener = parser.matched == "\""
|
22
|
+
|
23
|
+
parser.scan(PATTERN)
|
24
|
+
result = parser.matched
|
25
|
+
|
26
|
+
parser.scan(close) if opener
|
27
|
+
|
28
|
+
new(result)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module ERLE
|
2
|
+
|
3
|
+
class Term
|
4
|
+
|
5
|
+
class << self
|
6
|
+
attr_accessor :open, :close, :patterns
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_accessor :input
|
10
|
+
attr_accessor :output
|
11
|
+
|
12
|
+
|
13
|
+
def self.patterns
|
14
|
+
@patterns ||= Set.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.pattern(p)
|
18
|
+
patterns.add(p)
|
19
|
+
ERLE::Registry.pattern(self, p)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.enclosure(one, two=one)
|
23
|
+
@open = one.freeze
|
24
|
+
@close = two.freeze
|
25
|
+
ERLE::Registry.enclosure(self, one, two)
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(input)
|
29
|
+
@input = input
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_ruby
|
33
|
+
@input
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.to_ruby(value)
|
37
|
+
case value
|
38
|
+
when Term
|
39
|
+
value.to_ruby
|
40
|
+
else
|
41
|
+
value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.until_any_closing
|
46
|
+
@until_any_closing ||= Regexp.new("[^#{ERLE::Registry.closings_regex.source}]*")
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.parse(parser)
|
50
|
+
|
51
|
+
patterns.find do |pattern|
|
52
|
+
parser.scan(pattern)
|
53
|
+
end
|
54
|
+
|
55
|
+
result = parser.matched
|
56
|
+
|
57
|
+
if close && !parser.scan(close)
|
58
|
+
raise parser.raise_unexpected_token("Expected term closure \" #{close.source} \"")
|
59
|
+
end
|
60
|
+
|
61
|
+
# # Make sure we kill any trailing close
|
62
|
+
# # TODO: Consider raising if no match?
|
63
|
+
# # TODO: Consider doing only if we started with an opening...
|
64
|
+
# parser.scan(ERLE::Registry.closings_regex)
|
65
|
+
# # binding.pry
|
66
|
+
new(result)
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module ERLE
|
2
|
+
|
3
|
+
class Tuple < Enum
|
4
|
+
enclosure /\{/, /\}/
|
5
|
+
@delimeter = ','
|
6
|
+
|
7
|
+
attr_accessor :key
|
8
|
+
|
9
|
+
def initialize(elements)
|
10
|
+
super
|
11
|
+
@key = Term.to_ruby(@terms.shift)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_ruby
|
15
|
+
@output ||= { @key => one_or_all(terms_to_ruby) }
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
module ERLE
|
23
|
+
|
24
|
+
class Luple < Enum
|
25
|
+
# register "[{", "}]"
|
26
|
+
|
27
|
+
def to_ruby
|
28
|
+
# @output = Enum.split("},{")
|
29
|
+
# arr = @output.map {|el|
|
30
|
+
# el.is_a?(Term) ? el.to_ruby : el
|
31
|
+
# }
|
32
|
+
# key = arr.delete_at(0)
|
33
|
+
# hash = {key => ( arr.length>1 ? arr: arr[0])}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
data/lib/erle/errors.rb
ADDED
data/lib/erle/parser.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
|
2
|
+
require 'erle/registry'
|
3
|
+
|
4
|
+
module ERLE
|
5
|
+
|
6
|
+
class Parser < StringScanner
|
7
|
+
|
8
|
+
UNPARSED = Object.new.freeze
|
9
|
+
IGNORE = %r(
|
10
|
+
(?:
|
11
|
+
//[^\n\r]*[\n\r]| # line comments
|
12
|
+
/\* # c-style comments
|
13
|
+
(?:
|
14
|
+
[^*/]| # normal chars
|
15
|
+
/[^*]| # slashes that do not start a nested comment
|
16
|
+
\*[^/]| # asterisks that do not end this comment
|
17
|
+
/(?=\*/) # single slash before this comment's end
|
18
|
+
)*
|
19
|
+
\*/ # the End of this comment
|
20
|
+
|[ \t\r\n]+ # whitespaces: space, horicontal tab, lf, cr
|
21
|
+
)+
|
22
|
+
)mx
|
23
|
+
|
24
|
+
INTEGER = /(-?0|-?[1-9]\d*)/
|
25
|
+
FLOAT = /(-?
|
26
|
+
(?:0|[1-9]\d*)
|
27
|
+
(?:
|
28
|
+
\.\d+(?i:e[+-]?\d+) |
|
29
|
+
\.\d+ |
|
30
|
+
(?i:e[+-]?\d+)
|
31
|
+
)
|
32
|
+
)/x
|
33
|
+
TRUE = /true/
|
34
|
+
FALSE = /false/
|
35
|
+
|
36
|
+
def initialize(source, opts = {})
|
37
|
+
opts ||= {}
|
38
|
+
# source = convert_encoding source
|
39
|
+
super source
|
40
|
+
end
|
41
|
+
|
42
|
+
alias source string
|
43
|
+
|
44
|
+
def parse
|
45
|
+
reset
|
46
|
+
obj = nil
|
47
|
+
|
48
|
+
until_done do
|
49
|
+
if eos?
|
50
|
+
raise_parsing_error
|
51
|
+
else
|
52
|
+
obj = parse_value
|
53
|
+
UNPARSED.equal?(obj) and raise_parsing_error
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
eos? or raise_parsing_error
|
58
|
+
obj
|
59
|
+
end
|
60
|
+
|
61
|
+
def skip_ignore
|
62
|
+
skip(IGNORE)
|
63
|
+
end
|
64
|
+
|
65
|
+
def until_done
|
66
|
+
result = nil
|
67
|
+
while !eos?
|
68
|
+
skip_ignore
|
69
|
+
result = yield if block_given?
|
70
|
+
skip_ignore
|
71
|
+
end
|
72
|
+
result
|
73
|
+
end
|
74
|
+
|
75
|
+
def peekaboo(peek = 30, back = 30)
|
76
|
+
string[pos-back, peek+back]
|
77
|
+
end
|
78
|
+
|
79
|
+
def raise_parsing_error(message = "source is not valid Erlang!")
|
80
|
+
# warn "Parsing =>\n#{string}"
|
81
|
+
raise ParserError, "Parsing =>\n#{string}\n#{message}"
|
82
|
+
end
|
83
|
+
|
84
|
+
def whereami?(peek = 30, back = 30)
|
85
|
+
back = [back, string.length - pos].sort[0]
|
86
|
+
"#{peekaboo(peek, back)}'\n#{"─" * (back)}┘"
|
87
|
+
end
|
88
|
+
|
89
|
+
def raise_unexpected_token(followup=nil)
|
90
|
+
message = "Unexpected token at:\n#{whereami?}"
|
91
|
+
message += "\n#{followup}" if followup
|
92
|
+
raise ParserError, message
|
93
|
+
end
|
94
|
+
|
95
|
+
def parse_value
|
96
|
+
# TODO: handle symbols
|
97
|
+
skip_ignore
|
98
|
+
|
99
|
+
case
|
100
|
+
when scan(TRUE)
|
101
|
+
true
|
102
|
+
when scan(FALSE)
|
103
|
+
false
|
104
|
+
when !eos? && scan(ERLE::Registry.openings_regex) # TODO: Take out !eos?
|
105
|
+
regex, term_class = ERLE::Registry.open_find(matched)
|
106
|
+
term_str = term_class.parse(self)
|
107
|
+
else
|
108
|
+
term_class, regex = ERLE::Registry.pattern_find do |p|
|
109
|
+
match?(p)
|
110
|
+
end
|
111
|
+
|
112
|
+
if term_class
|
113
|
+
term_class.parse(self)
|
114
|
+
else
|
115
|
+
UNPARSED
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module ERLE
|
2
|
+
|
3
|
+
module Registry
|
4
|
+
class << self
|
5
|
+
attr_accessor :_enclosures
|
6
|
+
attr_accessor :_patterns
|
7
|
+
end
|
8
|
+
|
9
|
+
@_enclosures = {}
|
10
|
+
@_patterns = {}
|
11
|
+
|
12
|
+
def self.enclosure(klass, one, two)
|
13
|
+
@_enclosures[one] = klass
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.pattern(klass, pat)
|
17
|
+
(@_patterns[klass] ||= Set.new).add(pat)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.pattern_find
|
21
|
+
return unless block_given?
|
22
|
+
@_patterns.each do |klass, patterns|
|
23
|
+
pattern = patterns.find do |pattern|
|
24
|
+
yield(pattern)
|
25
|
+
end
|
26
|
+
return [klass, pattern] if pattern
|
27
|
+
end
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.open_find(str)
|
32
|
+
sorted_encolsures.find do |k, v|
|
33
|
+
str =~ k
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.sorted_encolsures
|
38
|
+
@sorted_encolsures ||= @_enclosures.sort_by { |pattern, klass| pattern.source.length }.reverse.to_h
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.openings
|
42
|
+
@openings ||= sorted_encolsures.keys
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.openings_source
|
46
|
+
@openings_source ||= openings.map(&:source)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.openings_regex
|
50
|
+
@openings_regex ||= Regexp.new("(#{openings_source.join('|')})")
|
51
|
+
end
|
52
|
+
|
53
|
+
# def self.close_find(str)
|
54
|
+
# @_enclosures.values.find do |klass|
|
55
|
+
# str =~ klass.close
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
|
59
|
+
# def self.closings
|
60
|
+
# @closings ||= @_enclosures.values.collect(&:close).sort { |a, b| b.source.length <=> a.source.length }
|
61
|
+
# end
|
62
|
+
|
63
|
+
# def self.closings_source
|
64
|
+
# @closings_source ||= closings.map(&:source)
|
65
|
+
# end
|
66
|
+
|
67
|
+
# def self.closings_regex
|
68
|
+
# @closings_regex ||= Regexp.new("(#{closings_source.join('|')})")
|
69
|
+
# end
|
70
|
+
end
|
71
|
+
end
|