rltk 1.2.0 → 2.0.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.
- data/README.md +610 -0
- data/Rakefile +156 -21
- data/lib/rltk/ast.rb +179 -127
- data/lib/rltk/cfg.rb +131 -38
- data/lib/rltk/cg/basic_block.rb +167 -0
- data/lib/rltk/cg/bindings.rb +136 -0
- data/lib/rltk/cg/builder.rb +1095 -0
- data/lib/rltk/cg/context.rb +51 -0
- data/lib/rltk/cg/execution_engine.rb +172 -0
- data/lib/rltk/cg/function.rb +224 -0
- data/lib/rltk/cg/generated_bindings.rb +6158 -0
- data/lib/rltk/cg/generated_extended_bindings.rb +43 -0
- data/lib/rltk/cg/generic_value.rb +98 -0
- data/lib/rltk/cg/instruction.rb +498 -0
- data/lib/rltk/cg/llvm.rb +51 -0
- data/lib/rltk/cg/memory_buffer.rb +62 -0
- data/lib/rltk/cg/module.rb +328 -0
- data/lib/rltk/cg/pass_manager.rb +201 -0
- data/lib/rltk/cg/support.rb +29 -0
- data/lib/rltk/cg/type.rb +510 -0
- data/lib/rltk/cg/value.rb +1207 -0
- data/lib/rltk/cg.rb +33 -0
- data/lib/rltk/lexer.rb +135 -85
- data/lib/rltk/lexers/calculator.rb +4 -1
- data/lib/rltk/lexers/ebnf.rb +1 -4
- data/lib/rltk/parser.rb +360 -196
- data/lib/rltk/token.rb +29 -4
- data/lib/rltk/util/abstract_class.rb +25 -0
- data/lib/rltk/util/monkeys.rb +125 -0
- data/lib/rltk/version.rb +5 -2
- data/test/tc_ast.rb +11 -11
- data/test/tc_lexer.rb +41 -41
- data/test/tc_parser.rb +123 -97
- data/test/ts_rltk.rb +50 -0
- metadata +181 -87
- data/README +0 -390
data/lib/rltk/cg.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Author: Chris Wailes <chris.wailes@gmail.com>
|
2
|
+
# Project: Ruby Language Toolkit
|
3
|
+
# Date: 2012/03/08
|
4
|
+
# Description: This file adds some autoload features for the RLTK code
|
5
|
+
# generation.
|
6
|
+
|
7
|
+
#######################
|
8
|
+
# Classes and Modules #
|
9
|
+
#######################
|
10
|
+
|
11
|
+
module RLTK # :nodoc:
|
12
|
+
|
13
|
+
# This module contains classes and methods for code generation. Code
|
14
|
+
# generation functionality is provided by bindings to
|
15
|
+
# [LLVM](http://llvm.org).
|
16
|
+
module CG
|
17
|
+
autoload :BasicBloc, 'rltk/cg/basic_block'
|
18
|
+
autoload :Bindings, 'rltk/cg/bindings'
|
19
|
+
autoload :Builder, 'rltk/cg/builder'
|
20
|
+
autoload :Context, 'rltk/cg/context'
|
21
|
+
autoload :ExecutionEngine, 'rltk/cg/execution_engine'
|
22
|
+
autoload :Function, 'rltk/cg/function'
|
23
|
+
autoload :GenericValue, 'rltk/cg/generic_value'
|
24
|
+
autoload :Instruction, 'rltk/cg/instruction'
|
25
|
+
autoload :LLVM, 'rltk/cg/llvm'
|
26
|
+
autoload :MemoryBuffer, 'rltk/cg/memory_buffer'
|
27
|
+
autoload :Module, 'rltk/cg/module'
|
28
|
+
autoload :PassManager, 'rltk/cg/pass_manager'
|
29
|
+
autoload :Support, 'rltk/cg/support'
|
30
|
+
autoload :Type, 'rltk/cg/type'
|
31
|
+
autoload :Value, 'rltk/cg/value'
|
32
|
+
end
|
33
|
+
end
|
data/lib/rltk/lexer.rb
CHANGED
@@ -21,7 +21,12 @@ module RLTK # :nodoc:
|
|
21
21
|
|
22
22
|
# A LexingError exception is raised when an input stream contains a
|
23
23
|
# substring that isn't matched by any of a lexer's rules.
|
24
|
-
class LexingError <
|
24
|
+
class LexingError < StandardError
|
25
|
+
|
26
|
+
# @param [Integer] stream_offset Offset from begnning of string.
|
27
|
+
# @param [Integer] line_number Number of newlines encountered so far.
|
28
|
+
# @param [Integer] line_offset Offset from beginning of line.
|
29
|
+
# @param [String] remainder Rest of the string that couldn't be lexed.
|
25
30
|
def initialize(stream_offset, line_number, line_offset, remainder)
|
26
31
|
@stream_offset = stream_offset
|
27
32
|
@line_number = line_number
|
@@ -29,7 +34,8 @@ module RLTK # :nodoc:
|
|
29
34
|
@remainder = remainder
|
30
35
|
end
|
31
36
|
|
32
|
-
|
37
|
+
# @return [String] String representation of the error.
|
38
|
+
def to_s
|
33
39
|
"#{super()}: #{@remainder}"
|
34
40
|
end
|
35
41
|
end
|
@@ -38,98 +44,61 @@ module RLTK # :nodoc:
|
|
38
44
|
# have a lot of features, and are described in the main documentation.
|
39
45
|
class Lexer
|
40
46
|
|
41
|
-
#
|
42
|
-
|
43
|
-
# instance methods.
|
44
|
-
def Lexer.inherited(klass)
|
45
|
-
klass.class_exec do
|
46
|
-
@core = LexerCore.new
|
47
|
-
|
48
|
-
# Returns this class's LexerCore object.
|
49
|
-
def self.core
|
50
|
-
@core
|
51
|
-
end
|
52
|
-
|
53
|
-
# Lexes the given string using a newly instantiated
|
54
|
-
# environment.
|
55
|
-
def self.lex(str)
|
56
|
-
@core.lex(str, self::Environment.new(@core.start_state))
|
57
|
-
end
|
58
|
-
|
59
|
-
# Lexes the contents of the given file using a newly
|
60
|
-
# instantiated environment.
|
61
|
-
def self.lex_file(file_name)
|
62
|
-
@core.lex_file(file_name, self::Environment.new(@core.start_state))
|
63
|
-
end
|
64
|
-
|
65
|
-
# Routes method calls to the new subclass to the LexerCore
|
66
|
-
# object.
|
67
|
-
def self.method_missing(method, *args, &proc)
|
68
|
-
@core.send(method, *args, &proc)
|
69
|
-
end
|
70
|
-
|
71
|
-
# Instantiates a new lexer and creates an environment to be
|
72
|
-
# used for subsequent calls.
|
73
|
-
def initialize
|
74
|
-
@env = self.class::Environment.new(self.class.core.start_state)
|
75
|
-
end
|
76
|
-
|
77
|
-
# Returns the environment used by an instantiated lexer.
|
78
|
-
def env
|
79
|
-
@env
|
80
|
-
end
|
81
|
-
|
82
|
-
# Lexes a string using the encapsulated environment.
|
83
|
-
def lex(string)
|
84
|
-
self.class.core.lex(string, @env)
|
85
|
-
end
|
86
|
-
|
87
|
-
# Lexes a file using the encapsulated environment.
|
88
|
-
def lex_file(file_name)
|
89
|
-
self.class.core.lex_file(file_name, @env)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
47
|
+
# @return [Environment] Environment used by an instantiated lexer.
|
48
|
+
attr_reader :env
|
93
49
|
|
94
50
|
#################
|
95
|
-
#
|
51
|
+
# Class Methods #
|
96
52
|
#################
|
97
53
|
|
98
|
-
|
99
|
-
|
100
|
-
# thereby allowing multiple lexers to be defined inside a single Ruby
|
101
|
-
# program.
|
102
|
-
class LexerCore
|
54
|
+
class << self
|
55
|
+
# @return [Symbol] State in which the lexer starts.
|
103
56
|
attr_reader :start_state
|
104
57
|
|
105
|
-
#
|
106
|
-
|
58
|
+
# Installs instance class varialbes into a class.
|
59
|
+
#
|
60
|
+
# @return [void]
|
61
|
+
def install_icvars
|
107
62
|
@match_type = :longest
|
108
63
|
@rules = Hash.new {|h,k| h[k] = Array.new}
|
109
64
|
@start_state = :default
|
110
65
|
end
|
111
66
|
|
112
|
-
#
|
67
|
+
# Called when the Lexer class is sub-classed, it installes
|
68
|
+
# necessary instance class variables.
|
69
|
+
#
|
70
|
+
# @return [void]
|
71
|
+
def inherited(klass)
|
72
|
+
klass.install_icvars
|
73
|
+
end
|
74
|
+
|
75
|
+
# Lex *string*, using *env* as the environment. This method will
|
113
76
|
# return the array of tokens generated by the lexer with a token
|
114
77
|
# of type EOS (End of Stream) appended to the end.
|
115
|
-
|
78
|
+
#
|
79
|
+
# @param [String] string String to be lexed.
|
80
|
+
# @param [String] file_name File name used for recording token positions.
|
81
|
+
# @param [Environment] env Lexing environment.
|
82
|
+
#
|
83
|
+
# @return [Array<Token>]
|
84
|
+
def lex(string, file_name = nil, env = self::Environment.new(@start_state))
|
116
85
|
# Offset from start of stream.
|
117
86
|
stream_offset = 0
|
118
|
-
|
87
|
+
|
119
88
|
# Offset from the start of the line.
|
120
89
|
line_offset = 0
|
121
90
|
line_number = 1
|
122
|
-
|
91
|
+
|
123
92
|
# Empty token list.
|
124
93
|
tokens = Array.new
|
125
|
-
|
94
|
+
|
126
95
|
# The scanner.
|
127
96
|
scanner = StringScanner.new(string)
|
128
|
-
|
97
|
+
|
129
98
|
# Start scanning the input string.
|
130
99
|
until scanner.eos?
|
131
100
|
match = nil
|
132
|
-
|
101
|
+
|
133
102
|
# If the match_type is set to :longest all of the
|
134
103
|
# rules for the current state need to be scanned
|
135
104
|
# and the longest match returned. If the
|
@@ -140,27 +109,27 @@ module RLTK # :nodoc:
|
|
140
109
|
if txt = scanner.check(rule.pattern)
|
141
110
|
if not match or match.first.length < txt.length
|
142
111
|
match = [txt, rule]
|
143
|
-
|
112
|
+
|
144
113
|
break if @match_type == :first
|
145
114
|
end
|
146
115
|
end
|
147
116
|
end
|
148
117
|
end
|
149
|
-
|
118
|
+
|
150
119
|
if match
|
151
120
|
rule = match.last
|
152
|
-
|
121
|
+
|
153
122
|
txt = scanner.scan(rule.pattern)
|
154
123
|
type, value = env.rule_exec(rule.pattern.match(txt), txt, &rule.action)
|
155
|
-
|
124
|
+
|
156
125
|
if type
|
157
126
|
pos = StreamPosition.new(stream_offset, line_number, line_offset, txt.length, file_name)
|
158
127
|
tokens << Token.new(type, value, pos)
|
159
128
|
end
|
160
|
-
|
129
|
+
|
161
130
|
# Advance our stat counters.
|
162
131
|
stream_offset += txt.length
|
163
|
-
|
132
|
+
|
164
133
|
if (newlines = txt.count("\n")) > 0
|
165
134
|
line_number += newlines
|
166
135
|
line_offset = 0
|
@@ -172,18 +141,25 @@ module RLTK # :nodoc:
|
|
172
141
|
raise(error, 'Unable to match string with any of the given rules')
|
173
142
|
end
|
174
143
|
end
|
175
|
-
|
144
|
+
|
176
145
|
return tokens << Token.new(:EOS)
|
177
146
|
end
|
178
147
|
|
179
|
-
# A wrapper function that calls
|
180
|
-
#
|
181
|
-
|
182
|
-
|
148
|
+
# A wrapper function that calls {Lexer.lex} on the contents of a
|
149
|
+
# file.
|
150
|
+
#
|
151
|
+
# @param [String] file_name File to be lexed.
|
152
|
+
# @param [Environment] env Lexing environment.
|
153
|
+
#
|
154
|
+
# @return [Array<Token>]
|
155
|
+
def lex_file(file_name, env = self::Environment.new(@start_state))
|
156
|
+
File.open(file_name, 'r') { |f| self.lex(f.read, file_name, env) }
|
183
157
|
end
|
184
158
|
|
185
159
|
# Used to tell a lexer to use the first match found instead
|
186
160
|
# of the longest match found.
|
161
|
+
#
|
162
|
+
# @return [void]
|
187
163
|
def match_first
|
188
164
|
@match_type = :first
|
189
165
|
end
|
@@ -197,6 +173,13 @@ module RLTK # :nodoc:
|
|
197
173
|
# type and value to be used in constructing a Token. If no
|
198
174
|
# block is specified the matched substring will be
|
199
175
|
# discarded and lexing will continue.
|
176
|
+
#
|
177
|
+
# @param [Regexp, String] pattern Pattern for matching text.
|
178
|
+
# @param [Symbol] state State in which this rule is active.
|
179
|
+
# @param [Array<Symbol>] flags Flags which must be set for rule to be active.
|
180
|
+
# @param [Proc] action Proc object that produces Tokens.
|
181
|
+
#
|
182
|
+
# @return [void]
|
200
183
|
def rule(pattern, state = :default, flags = [], &action)
|
201
184
|
# If no action is given we will set it to an empty
|
202
185
|
# action.
|
@@ -208,28 +191,63 @@ module RLTK # :nodoc:
|
|
208
191
|
|
209
192
|
if state == :ALL then @rules.each_key { |k| @rules[k] << r } else @rules[state] << r end
|
210
193
|
end
|
211
|
-
|
212
194
|
alias :r :rule
|
213
195
|
|
214
196
|
# Changes the starting state of the lexer.
|
197
|
+
#
|
198
|
+
# @param [Symbol] state Starting state for this lexer.
|
199
|
+
#
|
200
|
+
# @return [void]
|
215
201
|
def start(state)
|
216
202
|
@start_state = state
|
217
203
|
end
|
218
204
|
end
|
219
205
|
|
206
|
+
####################
|
207
|
+
# Instance Methods #
|
208
|
+
####################
|
209
|
+
|
210
|
+
# Instantiates a new lexer and creates an environment to be
|
211
|
+
# used for subsequent calls.
|
212
|
+
def initialize
|
213
|
+
@env = self.class::Environment.new(self.class.start_state)
|
214
|
+
end
|
215
|
+
|
216
|
+
# Lexes a string using the encapsulated environment.
|
217
|
+
#
|
218
|
+
# @param [String] string String to be lexed.
|
219
|
+
# @param [String] file_name File name used for Token positions.
|
220
|
+
#
|
221
|
+
# @return [Array<Token>]
|
222
|
+
def lex(string, file_name = nil)
|
223
|
+
self.class.lex(string, file_name, @env)
|
224
|
+
end
|
225
|
+
|
226
|
+
# Lexes a file using the encapsulated environment.
|
227
|
+
#
|
228
|
+
# @param [String] file_name File to be lexed.
|
229
|
+
#
|
230
|
+
# @return [Array<Token>]
|
231
|
+
def lex_file(file_name)
|
232
|
+
self.class.lex_file(file_name, @env)
|
233
|
+
end
|
234
|
+
|
220
235
|
# All actions passed to LexerCore.rule are evaluated inside an
|
221
236
|
# instance of the Environment class or its subclass (which must have
|
222
237
|
# the same name). This class provides functions for manipulating
|
223
238
|
# lexer state and flags.
|
224
239
|
class Environment
|
225
240
|
|
226
|
-
#
|
241
|
+
# @return [Array<Symbol>] Flags currently set in this environment.
|
227
242
|
attr_reader :flags
|
228
243
|
|
229
|
-
#
|
244
|
+
# @return [Match] Match object generated by a rule's regular expression.
|
230
245
|
attr_accessor :match
|
231
246
|
|
232
247
|
# Instantiates a new Environment object.
|
248
|
+
#
|
249
|
+
# @param [Symbol] start_state Lexer's start state.
|
250
|
+
# @param [Match] match Match object for matching text.
|
233
251
|
def initialize(start_state, match = nil)
|
234
252
|
@state = [start_state]
|
235
253
|
@match = match
|
@@ -238,6 +256,10 @@ module RLTK # :nodoc:
|
|
238
256
|
|
239
257
|
# This function will instance_exec a block for a rule after
|
240
258
|
# setting the match value.
|
259
|
+
#
|
260
|
+
# @param [Match] match Match object for matching text.
|
261
|
+
# @param [String] txt Text of matching string.
|
262
|
+
# @param [Proc] block Block for matched rule.
|
241
263
|
def rule_exec(match, txt, &block)
|
242
264
|
self.match = match
|
243
265
|
|
@@ -245,6 +267,8 @@ module RLTK # :nodoc:
|
|
245
267
|
end
|
246
268
|
|
247
269
|
# Pops a state from the state stack.
|
270
|
+
#
|
271
|
+
# @return [void]
|
248
272
|
def pop_state
|
249
273
|
@state.pop
|
250
274
|
|
@@ -252,6 +276,8 @@ module RLTK # :nodoc:
|
|
252
276
|
end
|
253
277
|
|
254
278
|
# Pushes a new state onto the state stack.
|
279
|
+
#
|
280
|
+
# @return [void]
|
255
281
|
def push_state(state)
|
256
282
|
@state << state
|
257
283
|
|
@@ -259,18 +285,26 @@ module RLTK # :nodoc:
|
|
259
285
|
end
|
260
286
|
|
261
287
|
# Sets the value on the top of the state stack.
|
288
|
+
#
|
289
|
+
# @param [Symbol] state New state for the lexing environment.
|
290
|
+
#
|
291
|
+
# @return [void]
|
262
292
|
def set_state(state)
|
263
293
|
@state[-1] = state
|
264
294
|
|
265
295
|
nil
|
266
296
|
end
|
267
297
|
|
268
|
-
#
|
298
|
+
# @return [Symbol] Current state of the lexing environment.
|
269
299
|
def state
|
270
|
-
|
300
|
+
@state.last
|
271
301
|
end
|
272
302
|
|
273
303
|
# Sets a flag in the current environment.
|
304
|
+
#
|
305
|
+
# @param [Symbol] flag Flag to set as enabled.
|
306
|
+
#
|
307
|
+
# @return [void]
|
274
308
|
def set_flag(flag)
|
275
309
|
if not @flags.include?(flag)
|
276
310
|
@flags << flag
|
@@ -280,6 +314,10 @@ module RLTK # :nodoc:
|
|
280
314
|
end
|
281
315
|
|
282
316
|
# Unsets a flag in the current environment.
|
317
|
+
#
|
318
|
+
# @param [Symbol] flag Flag to unset.
|
319
|
+
#
|
320
|
+
# @return [void]
|
283
321
|
def unset_flag(flag)
|
284
322
|
@flags.delete(flag)
|
285
323
|
|
@@ -287,6 +325,8 @@ module RLTK # :nodoc:
|
|
287
325
|
end
|
288
326
|
|
289
327
|
# Unsets all flags in the current environment.
|
328
|
+
#
|
329
|
+
# @return [void]
|
290
330
|
def clear_flags
|
291
331
|
@flags = Array.new
|
292
332
|
|
@@ -296,11 +336,21 @@ module RLTK # :nodoc:
|
|
296
336
|
|
297
337
|
# The Rule class is used simply for data encapsulation.
|
298
338
|
class Rule
|
339
|
+
# @return [Proc] Token producting action to be taken when this rule is matched.
|
299
340
|
attr_reader :action
|
341
|
+
|
342
|
+
# @return [Regexp] Regular expression for matching this rule.
|
300
343
|
attr_reader :pattern
|
344
|
+
|
345
|
+
# @return [Array<Symbol>] Flags currently set in this lexing environment.
|
301
346
|
attr_reader :flags
|
302
347
|
|
303
348
|
# Instantiates a new Rule object.
|
349
|
+
#
|
350
|
+
# @param [Regexp] pattern Regular expression used to match to this rule.
|
351
|
+
# @param [Proc] action Token producing action associated with this rule.
|
352
|
+
# @param [Symbol] state State in which this rule is active.
|
353
|
+
# @param [Array<Symbol>] flags Flags that must be enabled for this rule to match.
|
304
354
|
def initialize(pattern, action, state, flags)
|
305
355
|
@pattern = pattern
|
306
356
|
@action = action
|
@@ -15,7 +15,10 @@ require 'rltk/lexer'
|
|
15
15
|
#######################
|
16
16
|
|
17
17
|
module RLTK # :nodoc:
|
18
|
-
|
18
|
+
|
19
|
+
# The RLTK::Lexers module contains the lexers that are included as part of
|
20
|
+
# the RLKT project.
|
21
|
+
module Lexers
|
19
22
|
|
20
23
|
# The Calculator lexer is a simple lexer for use with several of the
|
21
24
|
# provided parsers.
|
data/lib/rltk/lexers/ebnf.rb
CHANGED
@@ -15,10 +15,7 @@ require 'rltk/lexer'
|
|
15
15
|
#######################
|
16
16
|
|
17
17
|
module RLTK # :nodoc:
|
18
|
-
|
19
|
-
# The RLTK::Lexers module contains the lexers that are included as part of
|
20
|
-
# the RLKT project.
|
21
|
-
module Lexers
|
18
|
+
module Lexers # :nodoc:
|
22
19
|
|
23
20
|
# The EBNF lexer is used by the RLTK::CFG class.
|
24
21
|
class EBNF < Lexer
|