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.
@@ -0,0 +1,9 @@
1
+ module ERLE
2
+
3
+ class List < Enum
4
+ enclosure /\[/, /\]/
5
+ @delimeter = ','
6
+
7
+ end
8
+
9
+ end
@@ -0,0 +1,9 @@
1
+ module ERLE
2
+
3
+ class Map < Enum
4
+ enclosure /\#\{/, /\}/
5
+ @delimeter = ','
6
+
7
+ end
8
+
9
+ end
@@ -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
@@ -0,0 +1,12 @@
1
+ module ERLE
2
+
3
+ class Error < StandardError
4
+ end
5
+
6
+ class ParserError < ERLE::Error
7
+ end
8
+
9
+ class NestingError < ParserError
10
+ end
11
+
12
+ end
@@ -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