rparsec2 1.1.0 → 1.2.1
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 +4 -4
- data/lib/rparsec/context.rb +80 -78
- data/lib/rparsec/error.rb +21 -18
- data/lib/rparsec/expressions.rb +75 -163
- data/lib/rparsec/functor_mixin.rb +102 -0
- data/lib/rparsec/functors.rb +151 -255
- data/lib/rparsec/id_monad.rb +13 -11
- data/lib/rparsec/keywords.rb +95 -93
- data/lib/rparsec/locator.rb +27 -25
- data/lib/rparsec/misc.rb +14 -97
- data/lib/rparsec/monad.rb +53 -50
- data/lib/rparsec/operator_table.rb +89 -0
- data/lib/rparsec/operators.rb +85 -81
- data/lib/rparsec/parser.rb +397 -819
- data/lib/rparsec/parser_monad.rb +18 -16
- data/lib/rparsec/parsers.rb +922 -499
- data/lib/rparsec/token.rb +32 -30
- data/lib/rparsec.rb +7 -4
- metadata +11 -3
data/lib/rparsec/locator.rb
CHANGED
@@ -1,40 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rparsec/misc'
|
2
4
|
|
3
5
|
module RParsec
|
4
6
|
|
5
|
-
class CodeLocator
|
6
|
-
|
7
|
+
class CodeLocator # :nodoc:
|
8
|
+
extend DefHelper
|
7
9
|
|
8
|
-
|
10
|
+
def_readable :code
|
9
11
|
|
10
|
-
|
12
|
+
LF = ?\n
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
14
|
+
def locate(ind)
|
15
|
+
return _locateEof if ind >= code.length
|
16
|
+
line, col = 1, 1
|
17
|
+
return line, col if ind <= 0
|
18
|
+
for i in (0...ind)
|
19
|
+
c = code[i]
|
20
|
+
if c == LF
|
21
|
+
line, col = line + 1, 1
|
22
|
+
else
|
23
|
+
col = col + 1
|
24
|
+
end
|
22
25
|
end
|
26
|
+
return line, col
|
23
27
|
end
|
24
|
-
return line, col
|
25
|
-
end
|
26
28
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
def _locateEof
|
30
|
+
line, col = 1, 1
|
31
|
+
code.each_byte do |c|
|
32
|
+
if c == LF.ord
|
33
|
+
line, col = line + 1, 1
|
34
|
+
else
|
35
|
+
col = col + 1
|
36
|
+
end
|
34
37
|
end
|
38
|
+
return line, col
|
35
39
|
end
|
36
|
-
return line, col
|
37
40
|
end
|
38
|
-
end
|
39
41
|
|
40
42
|
end # module
|
data/lib/rparsec/misc.rb
CHANGED
@@ -1,106 +1,23 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
#
|
4
|
-
# Helpers for defining ctor.
|
5
|
-
#
|
6
|
-
module DefHelper
|
7
|
-
def def_ctor(*vars)
|
8
|
-
define_method(:initialize) do |*params|
|
9
|
-
vars.each_with_index do |var, i|
|
10
|
-
instance_variable_set("@" + var.to_s, params[i])
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def def_readable(*vars)
|
16
|
-
attr_reader(*vars)
|
17
|
-
def_ctor(*vars)
|
18
|
-
end
|
1
|
+
# frozen_string_literal: true
|
19
2
|
|
20
|
-
|
21
|
-
attr_accessor(*vars)
|
22
|
-
def_ctor(*vars)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
#
|
27
|
-
# To type check method parameters.
|
28
|
-
#
|
29
|
-
module TypeChecker
|
30
|
-
private
|
31
|
-
|
32
|
-
def nth n
|
33
|
-
th = case n when 0 then 'st' when 1 then 'nd' else 'th' end
|
34
|
-
"#{n + 1}#{th}"
|
35
|
-
end
|
36
|
-
|
37
|
-
public
|
38
|
-
|
39
|
-
def check_arg_type expected, obj, mtd, n = 0
|
40
|
-
unless obj.kind_of? expected
|
41
|
-
raise ArgumentError,
|
42
|
-
"#{obj.class} assigned to #{expected} for the #{nth n} argument of #{mtd}."
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def check_arg_array_type elem_type, arg, mtd, n = 0
|
47
|
-
check_arg_type Array, arg, mtd, n
|
48
|
-
arg.each_with_index do |x, i|
|
49
|
-
unless x.kind_of? elem_type
|
50
|
-
raise ArgumentError,
|
51
|
-
"#{x.class} assigned to #{elem_type} for the #{nth i} element of the #{nth n} argument of #{mtd}."
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def check_vararg_type expected, args, mtd, n = 0
|
57
|
-
(n...args.length).each do |i|
|
58
|
-
check_arg_type expected, args[i], mtd, i
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
extend self
|
63
|
-
end
|
3
|
+
module RParsec
|
64
4
|
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
module
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
TypeChecker.check_arg_type Class, t, :def_sig, i unless t.length <= 1
|
75
|
-
TypeChecker.check_arg_array_type Class, t, :def_sig, i
|
5
|
+
#
|
6
|
+
# Helpers for defining ctor.
|
7
|
+
#
|
8
|
+
module DefHelper # :nodoc:
|
9
|
+
def def_ctor(*vars)
|
10
|
+
define_method(:initialize) do |*params|
|
11
|
+
vars.each_with_index do |var, i|
|
12
|
+
instance_variable_set("@" + var.to_s, params[i])
|
13
|
+
end
|
76
14
|
end
|
77
15
|
end
|
78
|
-
# Signatures[sym] = types
|
79
|
-
__intercept_method_to_check_param_types__(sym, types)
|
80
|
-
end
|
81
16
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
mtd = instance_method(sym)
|
86
|
-
define_method(sym) do |*params, &block|
|
87
|
-
star_type, star_ind = nil, nil
|
88
|
-
types.each_with_index do |t, i|
|
89
|
-
t = star_type unless star_type.nil?
|
90
|
-
arg = params[i]
|
91
|
-
if t.kind_of? Class
|
92
|
-
TypeChecker.check_arg_type t, arg, sym, i
|
93
|
-
elsif t.empty?
|
94
|
-
TypeChecker.check_arg_type Array, arg, sym, i
|
95
|
-
else
|
96
|
-
star_type, star_ind = t[0], i
|
97
|
-
break
|
98
|
-
end
|
99
|
-
end
|
100
|
-
TypeChecker.check_vararg_type star_type, params, sym, star_ind unless star_ind.nil?
|
101
|
-
mtd.bind(self).call(*params, &block)
|
17
|
+
def def_readable(*vars)
|
18
|
+
attr_reader(*vars)
|
19
|
+
def_ctor(*vars)
|
102
20
|
end
|
103
21
|
end
|
104
|
-
end
|
105
22
|
|
106
23
|
end # module
|
data/lib/rparsec/monad.rb
CHANGED
@@ -1,63 +1,66 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
# module for Monad
|
5
|
-
#
|
6
|
-
module Monad
|
7
|
-
attr_reader :this
|
3
|
+
module RParsec
|
8
4
|
|
9
5
|
#
|
10
|
-
#
|
6
|
+
# module for Monad
|
11
7
|
#
|
12
|
-
|
13
|
-
|
14
|
-
@monad = m;
|
15
|
-
@this = v;
|
16
|
-
end
|
8
|
+
module Monad
|
9
|
+
attr_reader :this
|
17
10
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
11
|
+
#
|
12
|
+
# To initialize with a monad implementation and an object that obeys the monad law.
|
13
|
+
#
|
14
|
+
def initMonad(m, v)
|
15
|
+
raise ArgumentError, 'monad cannot be nil' if m.nil?
|
16
|
+
@monad = m;
|
17
|
+
@this = v;
|
18
|
+
end
|
24
19
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
20
|
+
#
|
21
|
+
# To create a value based on the monad impl.
|
22
|
+
#
|
23
|
+
def value v
|
24
|
+
@monad.value v
|
25
|
+
end
|
31
26
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
if @monad.respond_to? :seq
|
38
|
-
@monad.seq(other)
|
39
|
-
else
|
40
|
-
bind { |_x| other }
|
27
|
+
#
|
28
|
+
# Run the _bind_ operation on the encapsulated object following the monad law.
|
29
|
+
#
|
30
|
+
def bind(&binder)
|
31
|
+
@monad.bind(@this, &binder)
|
41
32
|
end
|
42
|
-
end
|
43
33
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
34
|
+
#
|
35
|
+
# Run the _seq_ operation on the encapsulated object following the
|
36
|
+
# monad law. If +seq+ is not defined by the monad impl, use #bind
|
37
|
+
# to implement.
|
38
|
+
#
|
39
|
+
def seq(other)
|
40
|
+
if @monad.respond_to? :seq
|
41
|
+
@monad.seq(other)
|
42
|
+
else
|
43
|
+
bind { |_x| other }
|
44
|
+
end
|
52
45
|
end
|
53
|
-
end
|
54
46
|
|
55
|
-
|
56
|
-
|
57
|
-
#
|
58
|
-
|
59
|
-
|
47
|
+
#
|
48
|
+
# Run the _map_ operation on the encapsulated object following the
|
49
|
+
# monad law. #bind is used to implement.
|
50
|
+
#
|
51
|
+
def map(&mapper)
|
52
|
+
bind do |v|
|
53
|
+
result = mapper.call v;
|
54
|
+
value(result);
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# Run the _plus_ operation on the encapsulated object following the MonadPlus law.
|
60
|
+
#
|
61
|
+
def plus other
|
62
|
+
@monad.mplus(@this, other.this)
|
63
|
+
end
|
60
64
|
end
|
61
|
-
end
|
62
65
|
|
63
|
-
end # module
|
66
|
+
end # module
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module RParsec
|
2
|
+
#
|
3
|
+
# This class holds information about operator precedences and
|
4
|
+
# associativities. #prefix, #postfix, #infixl, #infixr, #infixn can
|
5
|
+
# be called to register operators.
|
6
|
+
#
|
7
|
+
class OperatorTable
|
8
|
+
|
9
|
+
#
|
10
|
+
# Operator attributes.
|
11
|
+
#
|
12
|
+
attr_reader :operators # :nodoc:
|
13
|
+
|
14
|
+
#
|
15
|
+
# Re-initialize the operator table.
|
16
|
+
#
|
17
|
+
def reinit # :nodoc:
|
18
|
+
@operators = []
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# To create an OperatorTable instance. If a block is given, it is
|
23
|
+
# invoked to do post-instantiation. For example:
|
24
|
+
#
|
25
|
+
# OperatorTable.new do |tbl|
|
26
|
+
# tbl.infixl(char(?+) >> Plus, 10)
|
27
|
+
# tbl.infixl(char(?-) >> Minus, 10)
|
28
|
+
# tbl.infixl(char(?*) >> Mul, 20)
|
29
|
+
# tbl.infixl(char(?/) >> Div, 20)
|
30
|
+
# tbl.prefix(char(?-) >> Neg, 50)
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
def self.new
|
34
|
+
this = allocate
|
35
|
+
this.reinit
|
36
|
+
if block_given?
|
37
|
+
yield this
|
38
|
+
end
|
39
|
+
this
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Defines a prefix operator that returns a unary +Proc+ object with a precedence associated.
|
44
|
+
# Returns +self+.
|
45
|
+
#
|
46
|
+
def prefix(op, precedence)
|
47
|
+
add(:prefix, op, precedence)
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Defines a postfix operator that returns a unary +Proc+ object with a precedence associated.
|
52
|
+
# Returns +self+.
|
53
|
+
#
|
54
|
+
def postfix(op, precedence)
|
55
|
+
add(:postfix, op, precedence)
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# Defines a left associative infix operator that returns a binary +Proc+ object with a precedence
|
60
|
+
# associated. Returns +self+.
|
61
|
+
#
|
62
|
+
def infixl(op, precedence)
|
63
|
+
add(:infixl, op, precedence)
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Defines a right associative infix operator that returns a binary +Proc+ object with a precedence
|
68
|
+
# associated. Returns +self+.
|
69
|
+
#
|
70
|
+
def infixr(op, precedence)
|
71
|
+
add(:infixr, op, precedence)
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# Defines a non-associative infix operator that returns a binary +Proc+ object with a precedence
|
76
|
+
# associated. Returns +self+.
|
77
|
+
#
|
78
|
+
def infixn(op, precedence)
|
79
|
+
add(:infixn, op, precedence)
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def add(*entry)
|
85
|
+
@operators << entry
|
86
|
+
self
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/rparsec/operators.rb
CHANGED
@@ -1,106 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rparsec/parser'
|
2
4
|
|
3
5
|
module RParsec
|
4
6
|
|
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
7
|
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
8
|
+
# This class helps building lexer and parser for operators. The
|
9
|
+
# case that one operator (<tt>++</tt> for example) contains another
|
10
|
+
# operator (<tt>+</tt>) is automatically handled so client code
|
11
|
+
# don't have to worry about ambiguity.
|
15
12
|
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
13
|
+
class Operators
|
14
|
+
#
|
15
|
+
# To create an instance of Operators for the given operators. The
|
16
|
+
# +block+ parameter, if present, is used to convert the token text
|
17
|
+
# to another object when the token is recognized during grammar
|
18
|
+
# parsing phase.
|
19
|
+
#
|
20
|
+
def initialize(ops, &block)
|
21
|
+
@lexers = {}
|
22
|
+
@parsers = {}
|
23
|
+
sorted = Operators.sort(ops)
|
24
|
+
lexers = sorted.map do |op|
|
25
|
+
symbol = op.to_sym
|
26
|
+
result = nil
|
27
|
+
if op.length == 1
|
28
|
+
result = Parsers.char(op)
|
29
|
+
else
|
30
|
+
result = Parsers.str(op)
|
31
|
+
end
|
32
|
+
result = result.token(symbol)
|
33
|
+
@lexers[symbol] = result
|
34
|
+
@parsers[symbol] = Parsers.token(symbol, &block)
|
35
|
+
result
|
27
36
|
end
|
28
|
-
|
29
|
-
@lexers[symbol] = result
|
30
|
-
@parsers[symbol] = Parsers.token(symbol, &block)
|
31
|
-
result
|
37
|
+
@lexer = Parsers.sum(*lexers)
|
32
38
|
end
|
33
|
-
@lexer = Parsers.sum(*lexers)
|
34
|
-
end
|
35
39
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
#
|
41
|
+
# Get the parser for the given operator.
|
42
|
+
#
|
43
|
+
def parser(op)
|
44
|
+
result = @parsers[op.to_sym]
|
45
|
+
raise ArgumentError, "parser not found for #{op}" if result.nil?
|
46
|
+
result
|
47
|
+
end
|
44
48
|
|
45
|
-
|
49
|
+
alias [] parser
|
46
50
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
51
|
+
#
|
52
|
+
# Get the lexer that lexes operators.
|
53
|
+
# If an operator is specified, the lexer for that operator is returned.
|
54
|
+
#
|
55
|
+
def lexer(op = nil)
|
56
|
+
return @lexer if op.nil?
|
57
|
+
@lexers[op.to_sym]
|
58
|
+
end
|
55
59
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
60
|
+
#
|
61
|
+
# Sort an array of operators so that contained operator appears after containers.
|
62
|
+
# When no containment exist between two operators, the shorter one takes precedence.
|
63
|
+
#
|
64
|
+
def self.sort(ops)
|
65
|
+
#sort the array by longer-string-first.
|
66
|
+
i = 0
|
67
|
+
ordered = ops.sort_by { |x| [x.length, i += 1] }.reverse
|
68
|
+
suites = []
|
69
|
+
# loop from the longer to shorter string
|
70
|
+
ordered.each do |s|
|
71
|
+
populate_suites(suites, s)
|
72
|
+
end
|
73
|
+
# suites are populated with bigger suite first
|
74
|
+
to_array suites
|
68
75
|
end
|
69
|
-
# suites are populated with bigger suite first
|
70
|
-
to_array suites
|
71
|
-
end
|
72
76
|
|
73
|
-
|
74
|
-
|
77
|
+
class << self
|
78
|
+
private
|
75
79
|
|
76
|
-
|
77
|
-
|
78
|
-
|
80
|
+
def populate_suites(suites, s)
|
81
|
+
# populate the suites so that bigger suite first
|
82
|
+
# this way we can use << operator for non-contained strings.
|
79
83
|
|
80
|
-
|
81
|
-
|
82
|
-
|
84
|
+
# we need to start from bigger suite. So loop in reverse order
|
85
|
+
for suite in suites
|
86
|
+
return if populate_suite(suite, s)
|
87
|
+
end
|
88
|
+
suites << [s]
|
83
89
|
end
|
84
|
-
suites << [s]
|
85
|
-
end
|
86
90
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
91
|
+
def populate_suite(suite, s)
|
92
|
+
# loop from the tail of the suite
|
93
|
+
for i in (1..suite.length)
|
94
|
+
ind = suite.length - i
|
95
|
+
cur = suite[ind]
|
96
|
+
if cur.start_with?(s)
|
97
|
+
suite.insert(ind + 1, s) unless cur == s
|
98
|
+
return true
|
99
|
+
end
|
95
100
|
end
|
101
|
+
false
|
96
102
|
end
|
97
|
-
false
|
98
|
-
end
|
99
103
|
|
100
|
-
|
101
|
-
|
104
|
+
def to_array suites
|
105
|
+
suites.reverse!.flatten!
|
106
|
+
end
|
102
107
|
end
|
103
108
|
end
|
104
|
-
end
|
105
109
|
|
106
110
|
end # module
|