rparsec 0.4.1 → 0.4.2
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.
- data/rparsec.rb +2 -2
- data/rparsec/context.rb +81 -66
- data/rparsec/error.rb +22 -19
- data/rparsec/expressions.rb +179 -136
- data/rparsec/functors.rb +270 -249
- data/rparsec/id_monad.rb +13 -11
- data/rparsec/keywords.rb +101 -89
- data/rparsec/locator.rb +36 -32
- data/rparsec/misc.rb +109 -102
- data/rparsec/monad.rb +57 -51
- data/rparsec/operators.rb +116 -109
- data/rparsec/parser.rb +892 -794
- data/rparsec/parser_monad.rb +19 -16
- data/rparsec/parsers.rb +614 -603
- data/rparsec/token.rb +23 -20
- data/test/src/simple_monad_test.rb +21 -21
- metadata +49 -41
data/rparsec/id_monad.rb
CHANGED
data/rparsec/keywords.rb
CHANGED
@@ -1,90 +1,102 @@
|
|
1
|
-
require 'rparsec/parser'
|
2
|
-
|
3
|
-
#
|
4
|
-
# This class helps building lexers and parsers for keywords.
|
5
|
-
#
|
6
|
-
class Keywords
|
7
|
-
extend Parsers
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
#
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
1
|
+
require 'rparsec/parser'
|
2
|
+
|
3
|
+
#
|
4
|
+
# This class helps building lexers and parsers for keywords.
|
5
|
+
#
|
6
|
+
class Keywords
|
7
|
+
extend Parsers
|
8
|
+
|
9
|
+
private_class_method :new
|
10
|
+
|
11
|
+
attr_reader :keyword_symbol, :lexer
|
12
|
+
|
13
|
+
#
|
14
|
+
# Do we lex case sensitively?
|
15
|
+
#
|
16
|
+
def case_sensitive?
|
17
|
+
@case_sensitive
|
18
|
+
end
|
19
|
+
|
20
|
+
#
|
21
|
+
# To create an instance that lexes the given keywords
|
22
|
+
# case sensitively.
|
23
|
+
# _default_lexer_ is used to lex a token first, the token text is then compared with
|
24
|
+
# the given keywords. If it matches any of the keyword, a keyword token is generated instead
|
25
|
+
# using _keyword_symbol_.
|
26
|
+
# The _block_ parameter, if present, is used to convert the token text to another object
|
27
|
+
# when the token is recognized during grammar parsing phase.
|
28
|
+
#
|
29
|
+
def self.case_sensitive(words, default_lexer=word.token(:word), keyword_symbol=:keyword, &block)
|
30
|
+
new(words, true, default_lexer, keyword_symbol, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# To create an instance that lexes the given keywords
|
35
|
+
# case insensitively.
|
36
|
+
# _default_lexer_ is used to lex a token first, the token text is then compared with
|
37
|
+
# the given keywords. If it matches any of the keyword, a keyword token is generated instead
|
38
|
+
# using _keyword_symbol_.
|
39
|
+
# The _block_ parameter, if present, is used to convert the token text to another object
|
40
|
+
# when the token is recognized during parsing phase.
|
41
|
+
#
|
42
|
+
def self.case_insensitive(words, default_lexer=word.token(:word), keyword_symbol=:keyword, &block)
|
43
|
+
new(words, false, default_lexer, keyword_symbol, &block)
|
44
|
+
end
|
45
|
+
|
46
|
+
# scanner has to return a string
|
47
|
+
def initialize(words, case_sensitive, default_lexer, keyword_symbol, &block)
|
48
|
+
@default_lexer, @case_sensitive, @keyword_symbol = default_lexer, case_sensitive, keyword_symbol
|
49
|
+
# this guarantees that we have copy of the words array and all the word strings.
|
50
|
+
words = copy_words(words, case_sensitive)
|
51
|
+
@name_map = {}
|
52
|
+
@symbol_map = {}
|
53
|
+
word_map = {}
|
54
|
+
words.each do |w|
|
55
|
+
symbol = "#{keyword_symbol}:#{w}".to_sym
|
56
|
+
word_map[w] = symbol
|
57
|
+
parser = Parsers.token(symbol, &block)
|
58
|
+
@symbol_map["#{w}".to_sym] = parser
|
59
|
+
@name_map[w] = parser
|
60
|
+
end
|
61
|
+
@lexer = make_lexer(default_lexer, word_map)
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Get the parser that recognizes the token of the given keyword during the parsing phase.
|
66
|
+
#
|
67
|
+
def parser(key)
|
68
|
+
result = nil
|
69
|
+
if key.kind_of? String
|
70
|
+
name = canonical_name(key)
|
71
|
+
result = @name_map[name]
|
72
|
+
else
|
73
|
+
result = @symbol_map[key]
|
74
|
+
end
|
75
|
+
raise ArgumentError, "parser not found for #{key}" if result.nil?
|
76
|
+
result
|
77
|
+
end
|
78
|
+
|
79
|
+
alias [] parser
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def make_lexer(default_lexer, word_map)
|
84
|
+
default_lexer.map do |tok|
|
85
|
+
text,ind = tok.text, tok.index
|
86
|
+
key = canonical_name(text)
|
87
|
+
my_symbol = word_map[key]
|
88
|
+
case when my_symbol.nil? then tok
|
89
|
+
else Token.new(my_symbol, text, ind) end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def canonical_name(name)
|
94
|
+
case when @case_sensitive then name else name.downcase end
|
95
|
+
end
|
96
|
+
|
97
|
+
def copy_words(words, case_sensitive)
|
98
|
+
words.map do |w|
|
99
|
+
case when case_sensitive then w.dup else w.downcase end
|
100
|
+
end
|
101
|
+
end
|
90
102
|
end
|
data/rparsec/locator.rb
CHANGED
@@ -1,32 +1,36 @@
|
|
1
|
-
require 'rparsec/misc'
|
2
|
-
|
3
|
-
class CodeLocator
|
4
|
-
extend DefHelper
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
line, col
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
1
|
+
require 'rparsec/misc'
|
2
|
+
|
3
|
+
class CodeLocator
|
4
|
+
extend DefHelper
|
5
|
+
|
6
|
+
def_readable :code
|
7
|
+
|
8
|
+
LF = ?\n
|
9
|
+
|
10
|
+
def locate(ind)
|
11
|
+
return _locateEof if ind >= code.length
|
12
|
+
line, col = 1,1
|
13
|
+
return line,col if ind<=0
|
14
|
+
for i in (0...ind)
|
15
|
+
c = code[i]
|
16
|
+
if c == LF
|
17
|
+
line, col = line+1, 1
|
18
|
+
else
|
19
|
+
col = col+1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
return line, col
|
23
|
+
end
|
24
|
+
|
25
|
+
def _locateEof
|
26
|
+
line, col = 1, 1
|
27
|
+
code.each_byte do |c|
|
28
|
+
if c == LF
|
29
|
+
line, col = line+1, 1
|
30
|
+
else
|
31
|
+
col = col+1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
return line, col
|
35
|
+
end
|
36
|
+
end
|
data/rparsec/misc.rb
CHANGED
@@ -1,102 +1,109 @@
|
|
1
|
-
#
|
2
|
-
# Helpers for defining ctor.
|
3
|
-
#
|
4
|
-
module DefHelper
|
5
|
-
def def_ctor(*vars)
|
6
|
-
define_method(:initialize) do |*params|
|
7
|
-
vars.each_with_index do |var, i|
|
8
|
-
instance_variable_set("@"+var.to_s, params[i])
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def def_readable(*vars)
|
14
|
-
attr_reader(*vars)
|
15
|
-
def_ctor(*vars)
|
16
|
-
end
|
17
|
-
|
18
|
-
def def_mutable(*vars)
|
19
|
-
attr_accessor(*vars)
|
20
|
-
def_ctor(*vars)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
#
|
25
|
-
# To type check method parameters.
|
26
|
-
#
|
27
|
-
module TypeChecker
|
28
|
-
private
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
end
|
102
|
-
|
1
|
+
#
|
2
|
+
# Helpers for defining ctor.
|
3
|
+
#
|
4
|
+
module DefHelper
|
5
|
+
def def_ctor(*vars)
|
6
|
+
define_method(:initialize) do |*params|
|
7
|
+
vars.each_with_index do |var, i|
|
8
|
+
instance_variable_set("@"+var.to_s, params[i])
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def def_readable(*vars)
|
14
|
+
attr_reader(*vars)
|
15
|
+
def_ctor(*vars)
|
16
|
+
end
|
17
|
+
|
18
|
+
def def_mutable(*vars)
|
19
|
+
attr_accessor(*vars)
|
20
|
+
def_ctor(*vars)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# To type check method parameters.
|
26
|
+
#
|
27
|
+
module TypeChecker
|
28
|
+
private
|
29
|
+
|
30
|
+
def nth n
|
31
|
+
th = case n when 0 then 'st' when 1 then 'nd' else 'th' end
|
32
|
+
"#{n+1}#{th}"
|
33
|
+
end
|
34
|
+
|
35
|
+
public
|
36
|
+
|
37
|
+
def check_arg_type expected, obj, mtd, n=0
|
38
|
+
unless obj.kind_of? expected
|
39
|
+
raise ArgumentError,
|
40
|
+
"#{obj.class} assigned to #{expected} for the #{nth n} argument of #{mtd}."
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def check_arg_array_type elem_type, arg, mtd, n=0
|
45
|
+
check_arg_type Array, arg, mtd, n
|
46
|
+
arg.each_with_index do |x, i|
|
47
|
+
unless x.kind_of? elem_type
|
48
|
+
raise ArgumentError,
|
49
|
+
"#{x.class} assigned to #{elem_type} for the #{nth i} element of the #{nth n} argument of #{mtd}."
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def check_vararg_type expected, args, mtd, n = 0
|
55
|
+
(n...args.length).each do |i|
|
56
|
+
check_arg_type expected, args[i], mtd, i
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
extend self
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# To add declarative signature support.
|
65
|
+
#
|
66
|
+
module Signature
|
67
|
+
# Signatures = {}
|
68
|
+
def def_sig sym, *types
|
69
|
+
types.each_with_index do |t,i|
|
70
|
+
unless t.kind_of? Class
|
71
|
+
TypeChecker.check_arg_type Class, t, :def_sig, i unless t.kind_of? Array
|
72
|
+
TypeChecker.check_arg_type Class, t, :def_sig, i unless t.length <= 1
|
73
|
+
TypeChecker.check_arg_array_type Class, t, :def_sig, i
|
74
|
+
end
|
75
|
+
end
|
76
|
+
# Signatures[sym] = types
|
77
|
+
__intercept_method_to_check_param_types__(sym, types)
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def __intercept_method_to_check_param_types__(sym, types)
|
83
|
+
mtd = instance_method(sym)
|
84
|
+
helper = "_#{sym}_param_types_checked_helper".to_sym
|
85
|
+
define_method(helper) do |*params|
|
86
|
+
star_type, star_ind = nil, nil
|
87
|
+
types.each_with_index do |t, i|
|
88
|
+
t = star_type unless star_type.nil?
|
89
|
+
arg = params[i]
|
90
|
+
if t.kind_of? Class
|
91
|
+
TypeChecker.check_arg_type t, arg, sym, i
|
92
|
+
elsif t.empty?
|
93
|
+
TypeChecker.check_arg_type Array, arg, sym, i
|
94
|
+
else
|
95
|
+
star_type, star_ind = t[0], i
|
96
|
+
break
|
97
|
+
end
|
98
|
+
end
|
99
|
+
TypeChecker.check_vararg_type star_type, params, sym, star_ind unless star_ind.nil?
|
100
|
+
mtd.bind(self)
|
101
|
+
end
|
102
|
+
module_eval """
|
103
|
+
def #{sym}(*params, &block)
|
104
|
+
#{helper}(*params).call(*params, &block)
|
105
|
+
end
|
106
|
+
"""
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|