rltk 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|