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