rparsec 0.4.1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|