rparsec2 1.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.
- checksums.yaml +7 -0
- data/lib/rparsec/context.rb +86 -0
- data/lib/rparsec/error.rb +28 -0
- data/lib/rparsec/expressions.rb +186 -0
- data/lib/rparsec/functors.rb +282 -0
- data/lib/rparsec/id_monad.rb +17 -0
- data/lib/rparsec/keywords.rb +114 -0
- data/lib/rparsec/locator.rb +40 -0
- data/lib/rparsec/misc.rb +106 -0
- data/lib/rparsec/monad.rb +63 -0
- data/lib/rparsec/operators.rb +106 -0
- data/lib/rparsec/parser.rb +893 -0
- data/lib/rparsec/parser_monad.rb +23 -0
- data/lib/rparsec/parsers.rb +624 -0
- data/lib/rparsec/token.rb +43 -0
- data/lib/rparsec.rb +7 -0
- metadata +59 -0
@@ -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.ord
|
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
|
data/lib/rparsec/misc.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
module RParsec
|
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
|
19
|
+
|
20
|
+
def def_mutable(*vars)
|
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
|
64
|
+
|
65
|
+
#
|
66
|
+
# To add declarative signature support.
|
67
|
+
#
|
68
|
+
module Signature
|
69
|
+
# Signatures = {}
|
70
|
+
def def_sig sym, *types
|
71
|
+
types.each_with_index do |t, i|
|
72
|
+
unless t.kind_of? Class
|
73
|
+
TypeChecker.check_arg_type Class, t, :def_sig, i unless t.kind_of? Array
|
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
|
76
|
+
end
|
77
|
+
end
|
78
|
+
# Signatures[sym] = types
|
79
|
+
__intercept_method_to_check_param_types__(sym, types)
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def __intercept_method_to_check_param_types__(sym, types)
|
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)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end # module
|
@@ -0,0 +1,63 @@
|
|
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
|
40
|
+
bind { |_x| other }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# Run the _map_ operation on the encapsulated object following the monad law.
|
46
|
+
# _bind_ is used to implement.
|
47
|
+
#
|
48
|
+
def map(&mapper)
|
49
|
+
bind do |v|
|
50
|
+
result = mapper.call v;
|
51
|
+
value(result);
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Run the _plus_ operation on the encapsulated object following the MonadPlus law.
|
57
|
+
#
|
58
|
+
def plus other
|
59
|
+
@monad.mplus(@this, other.this)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end # module
|
@@ -0,0 +1,106 @@
|
|
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
|
+
i = 0
|
63
|
+
ordered = ops.sort_by { |x| [x.length, i += 1] }.reverse
|
64
|
+
suites = []
|
65
|
+
# loop from the longer to shorter string
|
66
|
+
ordered.each do |s|
|
67
|
+
populate_suites(suites, s)
|
68
|
+
end
|
69
|
+
# suites are populated with bigger suite first
|
70
|
+
to_array suites
|
71
|
+
end
|
72
|
+
|
73
|
+
class << self
|
74
|
+
private
|
75
|
+
|
76
|
+
def populate_suites(suites, s)
|
77
|
+
# populate the suites so that bigger suite first
|
78
|
+
# this way we can use << operator for non-contained strings.
|
79
|
+
|
80
|
+
# we need to start from bigger suite. So loop in reverse order
|
81
|
+
for suite in suites
|
82
|
+
return if populate_suite(suite, s)
|
83
|
+
end
|
84
|
+
suites << [s]
|
85
|
+
end
|
86
|
+
|
87
|
+
def populate_suite(suite, s)
|
88
|
+
# loop from the tail of the suite
|
89
|
+
for i in (1..suite.length)
|
90
|
+
ind = suite.length - i
|
91
|
+
cur = suite[ind]
|
92
|
+
if cur.start_with?(s)
|
93
|
+
suite.insert(ind + 1, s) unless cur == s
|
94
|
+
return true
|
95
|
+
end
|
96
|
+
end
|
97
|
+
false
|
98
|
+
end
|
99
|
+
|
100
|
+
def to_array suites
|
101
|
+
suites.reverse!.flatten!
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end # module
|