rparsec-ruby19 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,17 @@
1
+ module RParsec
2
+
3
+ class IdMonad
4
+ def value v
5
+ v
6
+ end
7
+
8
+ def bind prev
9
+ yield prev
10
+ end
11
+
12
+ def mplus a, b
13
+ a
14
+ end
15
+ end
16
+
17
+ end # module
@@ -0,0 +1,114 @@
1
+ require 'rparsec/parser'
2
+
3
+ module RParsec
4
+
5
+ #
6
+ # This class helps building lexers and parsers for keywords.
7
+ #
8
+ class Keywords
9
+ extend Parsers
10
+
11
+ private_class_method :new
12
+
13
+ #
14
+ # The symbol used to identify a keyword token
15
+ #
16
+ attr_reader :keyword_symbol
17
+
18
+ #
19
+ # The lexer that parses all the keywords represented
20
+ #
21
+ attr_reader :lexer
22
+
23
+ #
24
+ # Do we lex case sensitively?
25
+ #
26
+ def case_sensitive?
27
+ @case_sensitive
28
+ end
29
+
30
+ #
31
+ # To create an instance that lexes the given keywords
32
+ # case sensitively.
33
+ # _default_lexer_ is used to lex a token first, the token text is then compared with
34
+ # the given keywords. If it matches any of the keyword, a keyword token is generated instead
35
+ # using _keyword_symbol_.
36
+ # The _block_ parameter, if present, is used to convert the token text to another object
37
+ # when the token is recognized during grammar parsing phase.
38
+ #
39
+ def self.case_sensitive(words, default_lexer=word.token(:word), keyword_symbol=:keyword, &block)
40
+ new(words, true, default_lexer, keyword_symbol, &block)
41
+ end
42
+
43
+ #
44
+ # To create an instance that lexes the given keywords
45
+ # case insensitively.
46
+ # _default_lexer_ is used to lex a token first, the token text is then compared with
47
+ # the given keywords. If it matches any of the keyword, a keyword token is generated instead
48
+ # using _keyword_symbol_.
49
+ # The _block_ parameter, if present, is used to convert the token text to another object
50
+ # when the token is recognized during parsing phase.
51
+ #
52
+ def self.case_insensitive(words, default_lexer=word.token(:word), keyword_symbol=:keyword, &block)
53
+ new(words, false, default_lexer, keyword_symbol, &block)
54
+ end
55
+
56
+ # scanner has to return a string
57
+ def initialize(words, case_sensitive, default_lexer, keyword_symbol, &block)
58
+ @default_lexer, @case_sensitive, @keyword_symbol = default_lexer, case_sensitive, keyword_symbol
59
+ # this guarantees that we have copy of the words array and all the word strings.
60
+ words = copy_words(words, case_sensitive)
61
+ @name_map = {}
62
+ @symbol_map = {}
63
+ word_map = {}
64
+ words.each do |w|
65
+ symbol = "#{keyword_symbol}:#{w}".to_sym
66
+ word_map[w] = symbol
67
+ parser = Parsers.token(symbol, &block)
68
+ @symbol_map["#{w}".to_sym] = parser
69
+ @name_map[w] = parser
70
+ end
71
+ @lexer = make_lexer(default_lexer, word_map)
72
+ end
73
+
74
+ #
75
+ # Get the parser that recognizes the token of the given keyword during the parsing phase.
76
+ #
77
+ def parser(key)
78
+ result = nil
79
+ if key.kind_of? String
80
+ name = canonical_name(key)
81
+ result = @name_map[name]
82
+ else
83
+ result = @symbol_map[key]
84
+ end
85
+ raise ArgumentError, "parser not found for #{key}" if result.nil?
86
+ result
87
+ end
88
+
89
+ alias [] parser
90
+
91
+ private
92
+
93
+ def make_lexer(default_lexer, word_map)
94
+ default_lexer.map do |tok|
95
+ text,ind = tok.text, tok.index
96
+ key = canonical_name(text)
97
+ my_symbol = word_map[key]
98
+ case when my_symbol.nil? then tok
99
+ else Token.new(my_symbol, text, ind) end
100
+ end
101
+ end
102
+
103
+ def canonical_name(name)
104
+ case when @case_sensitive then name else name.downcase end
105
+ end
106
+
107
+ def copy_words(words, case_sensitive)
108
+ words.map do |w|
109
+ case when case_sensitive then w.dup else w.downcase end
110
+ end
111
+ end
112
+ end
113
+
114
+ end # module
@@ -0,0 +1,40 @@
1
+ require 'rparsec/misc'
2
+
3
+ module RParsec
4
+
5
+ class CodeLocator
6
+ extend DefHelper
7
+
8
+ def_readable :code
9
+
10
+ LF = ?\n
11
+
12
+ def locate(ind)
13
+ return _locateEof if ind >= code.length
14
+ line, col = 1,1
15
+ return line,col if ind<=0
16
+ for i in (0...ind)
17
+ c = code[i]
18
+ if c == LF
19
+ line, col = line+1, 1
20
+ else
21
+ col = col+1
22
+ end
23
+ end
24
+ return line, col
25
+ end
26
+
27
+ def _locateEof
28
+ line, col = 1, 1
29
+ code.each_byte do |c|
30
+ if c == LF
31
+ line, col = line+1, 1
32
+ else
33
+ col = col+1
34
+ end
35
+ end
36
+ return line, col
37
+ end
38
+ end
39
+
40
+ end # module
@@ -0,0 +1,130 @@
1
+ module RParsec
2
+
3
+ #
4
+ # Internal utility functions for string manipulations.
5
+ #
6
+ module StringUtils
7
+ #
8
+ # Does _str_ starts with the _sub_ string?
9
+ #
10
+ def self.starts_with? str, sub
11
+ return true if sub.nil?
12
+ len = sub.length
13
+ return false if len > str.length
14
+ for i in (0...len)
15
+ return false if str[i] != sub[i]
16
+ end
17
+ true
18
+ end
19
+ end
20
+
21
+ #
22
+ # Helpers for defining ctor.
23
+ #
24
+ module DefHelper
25
+ def def_ctor(*vars)
26
+ define_method(:initialize) do |*params|
27
+ vars.each_with_index do |var, i|
28
+ instance_variable_set("@"+var.to_s, params[i])
29
+ end
30
+ end
31
+ end
32
+
33
+ def def_readable(*vars)
34
+ attr_reader(*vars)
35
+ def_ctor(*vars)
36
+ end
37
+
38
+ def def_mutable(*vars)
39
+ attr_accessor(*vars)
40
+ def_ctor(*vars)
41
+ end
42
+ end
43
+
44
+ #
45
+ # To type check method parameters.
46
+ #
47
+ module TypeChecker
48
+ private
49
+
50
+ def nth n
51
+ th = case n when 0 then 'st' when 1 then 'nd' else 'th' end
52
+ "#{n+1}#{th}"
53
+ end
54
+
55
+ public
56
+
57
+ def check_arg_type expected, obj, mtd, n=0
58
+ unless obj.kind_of? expected
59
+ raise ArgumentError,
60
+ "#{obj.class} assigned to #{expected} for the #{nth n} argument of #{mtd}."
61
+ end
62
+ end
63
+
64
+ def check_arg_array_type elem_type, arg, mtd, n=0
65
+ check_arg_type Array, arg, mtd, n
66
+ arg.each_with_index do |x, i|
67
+ unless x.kind_of? elem_type
68
+ raise ArgumentError,
69
+ "#{x.class} assigned to #{elem_type} for the #{nth i} element of the #{nth n} argument of #{mtd}."
70
+ end
71
+ end
72
+ end
73
+
74
+ def check_vararg_type expected, args, mtd, n = 0
75
+ (n...args.length).each do |i|
76
+ check_arg_type expected, args[i], mtd, i
77
+ end
78
+ end
79
+
80
+ extend self
81
+ end
82
+
83
+ #
84
+ # To add declarative signature support.
85
+ #
86
+ module Signature
87
+ # Signatures = {}
88
+ def def_sig sym, *types
89
+ types.each_with_index do |t,i|
90
+ unless t.kind_of? Class
91
+ TypeChecker.check_arg_type Class, t, :def_sig, i unless t.kind_of? Array
92
+ TypeChecker.check_arg_type Class, t, :def_sig, i unless t.length <= 1
93
+ TypeChecker.check_arg_array_type Class, t, :def_sig, i
94
+ end
95
+ end
96
+ # Signatures[sym] = types
97
+ __intercept_method_to_check_param_types__(sym, types)
98
+ end
99
+
100
+ private
101
+
102
+ def __intercept_method_to_check_param_types__(sym, types)
103
+ mtd = instance_method(sym)
104
+ helper = "_#{sym}_param_types_checked_helper".to_sym
105
+ define_method(helper) do |*params|
106
+ star_type, star_ind = nil, nil
107
+ types.each_with_index do |t, i|
108
+ t = star_type unless star_type.nil?
109
+ arg = params[i]
110
+ if t.kind_of? Class
111
+ TypeChecker.check_arg_type t, arg, sym, i
112
+ elsif t.empty?
113
+ TypeChecker.check_arg_type Array, arg, sym, i
114
+ else
115
+ star_type, star_ind = t[0], i
116
+ break
117
+ end
118
+ end
119
+ TypeChecker.check_vararg_type star_type, params, sym, star_ind unless star_ind.nil?
120
+ mtd.bind(self)
121
+ end
122
+ module_eval """
123
+ def #{sym}(*params, &block)
124
+ #{helper}(*params).call(*params, &block)
125
+ end
126
+ """
127
+ end
128
+ end
129
+
130
+ end # module
@@ -0,0 +1,62 @@
1
+ module RParsec
2
+
3
+ #
4
+ # module for Monad
5
+ #
6
+ module Monad
7
+ attr_reader :this
8
+
9
+ #
10
+ # To initialize with a monad implementation and an object that obeys the monad law.
11
+ #
12
+ def initMonad(m, v)
13
+ raise ArgumentError, 'monad cannot be nil' if m.nil?
14
+ @monad = m;
15
+ @this = v;
16
+ end
17
+
18
+ #
19
+ # To create a value based on the monad impl.
20
+ #
21
+ def value v
22
+ @monad.value v
23
+ end
24
+
25
+ #
26
+ # Run the _bind_ operation on the encapsulated object following the monad law.
27
+ #
28
+ def bind(&binder)
29
+ @monad.bind(@this, &binder)
30
+ end
31
+
32
+ #
33
+ # Run the _seq_ operation on the encapsulated object following the monad law.
34
+ # If _seq_ is not defined by the monad impl, use _bind_ to implement.
35
+ #
36
+ def seq(other)
37
+ if @monad.respond_to? :seq
38
+ @monad.seq(other)
39
+ else bind {|x|other}
40
+ end
41
+ end
42
+
43
+ #
44
+ # Run the _map_ operation on the encapsulated object following the monad law.
45
+ # _bind_ is used to implement.
46
+ #
47
+ def map(&mapper)
48
+ bind do |v|
49
+ result = mapper.call v;
50
+ value(result);
51
+ end
52
+ end
53
+
54
+ #
55
+ # Run the _plus_ operation on the encapsulated object following the MonadPlus law.
56
+ #
57
+ def plus other
58
+ @monad.mplus(@this, other.this)
59
+ end
60
+ end
61
+
62
+ end # module
@@ -0,0 +1,103 @@
1
+ require 'rparsec/parser'
2
+
3
+ module RParsec
4
+
5
+ #
6
+ # This class helps building lexer and parser for operators.
7
+ # The case that one operator (++ for example) contains another operator (+)
8
+ # is automatically handled so client code don't have to worry about ambiguity.
9
+ #
10
+ class Operators
11
+ #
12
+ # To create an instance of Operators for the given operators.
13
+ # The _block_ parameter, if present, is used to convert the token text to another object
14
+ # when the token is recognized during grammar parsing phase.
15
+ #
16
+ def initialize(ops, &block)
17
+ @lexers = {}
18
+ @parsers = {}
19
+ sorted = Operators.sort(ops)
20
+ lexers = sorted.map do |op|
21
+ symbol = op.to_sym
22
+ result = nil
23
+ if op.length == 1
24
+ result = Parsers.char(op)
25
+ else
26
+ result = Parsers.str(op)
27
+ end
28
+ result = result.token(symbol)
29
+ @lexers[symbol] = result
30
+ @parsers[symbol] = Parsers.token(symbol, &block)
31
+ result
32
+ end
33
+ @lexer = Parsers.sum(*lexers)
34
+ end
35
+
36
+ #
37
+ # Get the parser for the given operator.
38
+ #
39
+ def parser(op)
40
+ result = @parsers[op.to_sym]
41
+ raise ArgumentError, "parser not found for #{op}" if result.nil?
42
+ result
43
+ end
44
+
45
+ alias [] parser
46
+
47
+ #
48
+ # Get the lexer that lexes operators.
49
+ # If an operator is specified, the lexer for that operator is returned.
50
+ #
51
+ def lexer(op=nil)
52
+ return @lexer if op.nil?
53
+ @lexers[op.to_sym]
54
+ end
55
+
56
+ #
57
+ # Sort an array of operators so that contained operator appears after containers.
58
+ # When no containment exist between two operators, the shorter one takes precedence.
59
+ #
60
+ def self.sort(ops)
61
+ #sort the array by longer-string-first.
62
+ ordered = ops.sort {|x, y|y.length <=> x.length}
63
+ suites = []
64
+ # loop from the longer to shorter string
65
+ ordered.each do |s|
66
+ populate_suites(suites, s)
67
+ end
68
+ # suites are populated with bigger suite first
69
+ to_array suites
70
+ end
71
+
72
+ private
73
+
74
+ def self.populate_suites(suites, s)
75
+ # populate the suites so that bigger suite first
76
+ # this way we can use << operator for non-contained strings.
77
+
78
+ # we need to start from bigger suite. So loop in reverse order
79
+ for suite in suites
80
+ return if populate_suite(suite, s)
81
+ end
82
+ suites << [s]
83
+ end
84
+
85
+ def self.populate_suite(suite, s)
86
+ # loop from the tail of the suite
87
+ for i in (1..suite.length)
88
+ ind = suite.length - i
89
+ cur = suite[ind]
90
+ if StringUtils.starts_with? cur, s
91
+ suite.insert(ind+1, s) unless cur == s
92
+ return true
93
+ end
94
+ end
95
+ false
96
+ end
97
+
98
+ def self.to_array suites
99
+ suites.reverse!.flatten!
100
+ end
101
+ end
102
+
103
+ end # module