trac_lang 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +58 -0
- data/Rakefile +12 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/examples/README.trl +633 -0
- data/examples/golf.trl +113 -0
- data/examples/list.trl +125 -0
- data/examples/math.trl +351 -0
- data/examples/meta.trl +254 -0
- data/examples/ratio.trl +107 -0
- data/examples/struct.trl +176 -0
- data/examples/term.trl +129 -0
- data/examples/util.trl +366 -0
- data/exe/trac_lang +74 -0
- data/lib/trac_lang.rb +16 -0
- data/lib/trac_lang/bindings.rb +53 -0
- data/lib/trac_lang/block.rb +46 -0
- data/lib/trac_lang/decimal.rb +79 -0
- data/lib/trac_lang/dispatch.rb +421 -0
- data/lib/trac_lang/executor.rb +97 -0
- data/lib/trac_lang/expression.rb +58 -0
- data/lib/trac_lang/form.rb +253 -0
- data/lib/trac_lang/immediate_read.rb +52 -0
- data/lib/trac_lang/octal.rb +88 -0
- data/lib/trac_lang/parser.rb +114 -0
- data/lib/trac_lang/version.rb +4 -0
- data/trac_lang.gemspec +42 -0
- metadata +177 -0
data/exe/trac_lang
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.dirname(__FILE__) + '/../lib'
|
4
|
+
require 'optparse'
|
5
|
+
require 'trac_lang'
|
6
|
+
|
7
|
+
USAGE_INSTRUCTIONS = ''
|
8
|
+
|
9
|
+
def parse_options
|
10
|
+
options = {}
|
11
|
+
op = OptionParser.new do |opt|
|
12
|
+
opt.banner = 'Usage: trac_lang [OPTIONS] file1.trl file2.trl...'
|
13
|
+
options[:save_dir] = Dir.pwd
|
14
|
+
options[:save_dir] = ENV['TRAC-HOME'] if ENV['TRAC-HOME']
|
15
|
+
opt.on('-d', '--save-dir', 'Directory to save TRAC blocks to') do |dir|
|
16
|
+
options[:save_dir] = dir
|
17
|
+
end
|
18
|
+
exit_on_eof = false
|
19
|
+
opt.on('-x', '--exit-on-eof', 'Exit when finished processing files') do
|
20
|
+
options[:exit_on_eof] = true
|
21
|
+
end
|
22
|
+
options[:trace] = false
|
23
|
+
opt.on('-t', '--trace', 'Turn on trace') do
|
24
|
+
options[:trace] = true
|
25
|
+
end
|
26
|
+
opt.on( '-h', '--help', 'Display this screen' ) do
|
27
|
+
puts opt
|
28
|
+
exit
|
29
|
+
end
|
30
|
+
end
|
31
|
+
USAGE_INSTRUCTIONS << op.to_s
|
32
|
+
op.parse!
|
33
|
+
options
|
34
|
+
end
|
35
|
+
|
36
|
+
def print_error(error)
|
37
|
+
case error
|
38
|
+
when OptionParser::InvalidOption
|
39
|
+
puts "trac_lang: illegal option #{error.args.join(' ')}"
|
40
|
+
puts USAGE_INSTRUCTIONS
|
41
|
+
else
|
42
|
+
puts "An unexpected error occured while running TRAC Lang"
|
43
|
+
puts " #{error}\n"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
begin
|
48
|
+
d = TracLang::Dispatch.new(parse_options)
|
49
|
+
e = TracLang::Executor.new(d)
|
50
|
+
catch :done do
|
51
|
+
last_file = ''
|
52
|
+
last_line = 0
|
53
|
+
current_line = 1
|
54
|
+
ARGF.each_line do |line|
|
55
|
+
if ARGF.filename != last_file
|
56
|
+
e.restore_dir unless last_file.empty?
|
57
|
+
e.save_dir(ARGF.filename)
|
58
|
+
current_line = 1
|
59
|
+
last_file = ARGF.filename
|
60
|
+
else
|
61
|
+
current_line += 1
|
62
|
+
end
|
63
|
+
e.load(ARGF.filename, current_line, line)
|
64
|
+
end
|
65
|
+
exit if parse_options[:exit_on_eof]
|
66
|
+
e.restore_dir
|
67
|
+
e.prompt
|
68
|
+
end
|
69
|
+
puts 'Exiting...'
|
70
|
+
puts
|
71
|
+
rescue => error
|
72
|
+
print_error(error)
|
73
|
+
exit(false)
|
74
|
+
end
|
data/lib/trac_lang.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative 'trac_lang/bindings'
|
2
|
+
require_relative 'trac_lang/block'
|
3
|
+
require_relative 'trac_lang/decimal'
|
4
|
+
require_relative 'trac_lang/dispatch'
|
5
|
+
require_relative 'trac_lang/executor'
|
6
|
+
require_relative 'trac_lang/expression'
|
7
|
+
require_relative 'trac_lang/form'
|
8
|
+
require_relative 'trac_lang/immediate_read'
|
9
|
+
require_relative 'trac_lang/octal'
|
10
|
+
require_relative 'trac_lang/parser'
|
11
|
+
require_relative 'trac_lang/version'
|
12
|
+
|
13
|
+
# Module containing code for processing TRAC language.
|
14
|
+
module TracLang
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module TracLang
|
4
|
+
|
5
|
+
# Binding of name to Form represented by the name.
|
6
|
+
class Bindings
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
# Hash map of names to Forms.
|
10
|
+
attr_reader :bindings
|
11
|
+
|
12
|
+
# Creates bindings from list of names and Forms.
|
13
|
+
def initialize(*bindings)
|
14
|
+
@bindings = Hash[bindings]
|
15
|
+
end
|
16
|
+
|
17
|
+
# Clears all bindings.
|
18
|
+
def clear
|
19
|
+
@bindings.clear
|
20
|
+
end
|
21
|
+
|
22
|
+
# Adds a binding to the map of bindings. Will replace a binding with the same name.
|
23
|
+
def add(*binding)
|
24
|
+
if binding[0].is_a? Array
|
25
|
+
@bindings.merge!(Hash[binding])
|
26
|
+
else
|
27
|
+
@bindings.merge!(Hash[[binding]])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Fetches the Form with the given name. Returns nil if no Form has the given name.
|
32
|
+
def fetch(name)
|
33
|
+
@bindings.fetch(name, nil)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Fetches the binding for the given name, or nil if nothing has the given name.
|
37
|
+
def fetch_binding(name)
|
38
|
+
f = @bindings.fetch(name, nil)
|
39
|
+
return f ? [name, f] : nil
|
40
|
+
end
|
41
|
+
|
42
|
+
# Unbinds any Form bound to the given name.
|
43
|
+
def delete(name)
|
44
|
+
@bindings.delete(name)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Passes each binding to the given block.
|
48
|
+
def each(&blk)
|
49
|
+
@bindings.each(&blk)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module TracLang
|
4
|
+
|
5
|
+
# Class for storing forms under their names.
|
6
|
+
class Block
|
7
|
+
|
8
|
+
# Reads block from the given file. The file may have any valid TRAC
|
9
|
+
# commands in it, as well as ordinary text, which will be ignored.
|
10
|
+
# The options for trace and savedir will be inherited from the Dispatch
|
11
|
+
# calling this method.
|
12
|
+
def self.read(filename, dispatch)
|
13
|
+
Executor.new(dispatch).load_file(filename)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Writes block to the given file. Block is written with the following:
|
17
|
+
# 1. Version of the TRAC Language processor
|
18
|
+
# 2. Current time
|
19
|
+
# 3. #(DS) commands for each bound form
|
20
|
+
# 4. #(SS) commands for each bound form that has segments
|
21
|
+
# 5. A mix of #(CN) and #(CS) commands to position the form pointer
|
22
|
+
def self.write(filename, bindings)
|
23
|
+
begin
|
24
|
+
File.open(filename, "w") do |f|
|
25
|
+
f.puts "TRAC Lang Version #{VERSION}"
|
26
|
+
f.puts "Saved: #{Time.now}"
|
27
|
+
f.puts
|
28
|
+
bindings.each { |name, form| f.puts(form.to_trac(name)) if form }
|
29
|
+
end
|
30
|
+
rescue
|
31
|
+
# do nothing if file open fails
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Deletes given file.
|
36
|
+
def self.delete(filename)
|
37
|
+
begin
|
38
|
+
File.delete(filename)
|
39
|
+
rescue Errno::ENOENT
|
40
|
+
# ignore non-existant file
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
|
2
|
+
module TracLang
|
3
|
+
|
4
|
+
# Integer for TRAC Language. Consists of
|
5
|
+
# * string prefix
|
6
|
+
# * sign
|
7
|
+
# * numeric value
|
8
|
+
# The string prefix can be used to label the
|
9
|
+
# number and is carried over by operations.
|
10
|
+
# The sign is separate from the numeric value
|
11
|
+
# so that -0 can be distinguished from +0.
|
12
|
+
# This is needed when testing for Form pointers
|
13
|
+
# for EndOfString.
|
14
|
+
class Decimal
|
15
|
+
|
16
|
+
# String prefix of this number. Used to label the number,
|
17
|
+
# such as +Apples5+ or +Balance-100+.
|
18
|
+
attr_accessor :prefix
|
19
|
+
|
20
|
+
# Flag for negativity. Needed to distinguish between -0 and +0.
|
21
|
+
attr_accessor :negative
|
22
|
+
|
23
|
+
# Numeric value of this number.
|
24
|
+
attr_accessor :value
|
25
|
+
|
26
|
+
alias_method :negative?, :negative
|
27
|
+
|
28
|
+
# Create a TRAC decimal from a string. Any leading characters are
|
29
|
+
# saved as a prefix. The last sign character before the numeric
|
30
|
+
# portion is sign. If there are no numeric characters in the given
|
31
|
+
# string, zero is assumed.
|
32
|
+
def initialize(str = '')
|
33
|
+
raise ArgumentError unless str.is_a? String
|
34
|
+
n = str.partition(/[+-]?[0-9]*$/)
|
35
|
+
@prefix = n[0]
|
36
|
+
@value = n[1].to_i
|
37
|
+
@negative = n[1][0] == '-'
|
38
|
+
end
|
39
|
+
|
40
|
+
# Tests for equality. This is different from numeric equality
|
41
|
+
# because prefixes are tested as well as the numeric value.
|
42
|
+
def ==(d)
|
43
|
+
return super unless d.is_a? TracLang::Decimal
|
44
|
+
d.prefix == @prefix && d.value == @value && d.negative == @negative
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns string value of decimal. A sign is added to negative zeros.
|
48
|
+
def to_s
|
49
|
+
prefix + (negative? && value == 0 ? '-' : '') + value.to_s
|
50
|
+
end
|
51
|
+
|
52
|
+
# Defines method for given arithmetical operation. Result has string
|
53
|
+
# prefix of self. The operations defined are:
|
54
|
+
# [+]
|
55
|
+
# Sum of two decimals.
|
56
|
+
# [-]
|
57
|
+
# Difference.
|
58
|
+
# [*]
|
59
|
+
# Product.
|
60
|
+
# [/]
|
61
|
+
# Quotient.
|
62
|
+
def self.define_operation(symbol)
|
63
|
+
define_method(symbol) do |other|
|
64
|
+
result = Decimal.new
|
65
|
+
result.prefix = prefix
|
66
|
+
result.value = value.send(symbol, other.value)
|
67
|
+
result.negative = result.value < 0
|
68
|
+
result
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
define_operation :+
|
73
|
+
define_operation :-
|
74
|
+
define_operation :*
|
75
|
+
define_operation :/
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,421 @@
|
|
1
|
+
|
2
|
+
require_relative 'block'
|
3
|
+
require_relative 'decimal'
|
4
|
+
require_relative 'expression'
|
5
|
+
require_relative 'form'
|
6
|
+
require_relative 'octal'
|
7
|
+
|
8
|
+
module TracLang
|
9
|
+
|
10
|
+
# Class to dispatch TRAC commands to the proper code. Also stores the following options needed to run TRAC:
|
11
|
+
# [trace] Determines whether to display TRAC commands before executing them
|
12
|
+
# [meta] Meta character used to signal the end of input
|
13
|
+
# [savedir] Directory to save blocks to and load them from
|
14
|
+
class Dispatch
|
15
|
+
|
16
|
+
class << self
|
17
|
+
# Dispatch table. All basic TRAC commands are stored in the dispatch table.
|
18
|
+
# This is done instead of methods to prevent someone from inadvertently (or
|
19
|
+
# on purpose) calling a Ruby internal method instead of a TRAC command.
|
20
|
+
attr_accessor :table
|
21
|
+
end
|
22
|
+
|
23
|
+
# Initialize dispatch table.
|
24
|
+
@table = {}
|
25
|
+
|
26
|
+
# Defines a TRAC command to be added to the dispatch table.
|
27
|
+
# The following commands are defined.
|
28
|
+
#
|
29
|
+
# ---
|
30
|
+
# <em>System Commands</em>
|
31
|
+
#
|
32
|
+
# [on :hl]
|
33
|
+
# Halt. Halts the TRAC processor.
|
34
|
+
# [on :tn]
|
35
|
+
# Trace On. While trace is on, each Expression to be executed will be displayed to the
|
36
|
+
# user first. If the user presses return, the Expression will be executed. If any other
|
37
|
+
# key is pressed, the active string will be cleared and the idle string will be reloaded.
|
38
|
+
# [on :tf]
|
39
|
+
# Trace Off. When trace is off, execution proceeds as normal.
|
40
|
+
# [on :pf do |name = ''|]
|
41
|
+
# Print Form. Prints the form with the given name on the console. The form text, segments, and form pointer
|
42
|
+
# will all be displayed.
|
43
|
+
#
|
44
|
+
# ---
|
45
|
+
# <em>Basic Commands</em>
|
46
|
+
#
|
47
|
+
# [on :ds do |name = '', value = ''|]
|
48
|
+
# Define String. Creates a new Form with the given name and value, and stores it in the current set of bindings.
|
49
|
+
# [on :eq do |str1 = '', str2 = '', t = '', f = ''|]
|
50
|
+
# Equal. Tests if two strings are equal and returns the string t or f depending on the result of the test. Note that
|
51
|
+
# this is a string test for equality, so will not work for numerics. To compare numerically see the greater than command.
|
52
|
+
# [on :gr do |num1 = '', num2 = '', t = '', f = ''|]
|
53
|
+
# Greater Than. Compares the given Decimal values and returns the string t or f depending on the result of the test. To
|
54
|
+
# test two Decimal values for equality, test if neither is greater than the other.
|
55
|
+
#
|
56
|
+
# ---
|
57
|
+
# <em>I/O Commands</em>
|
58
|
+
#
|
59
|
+
# [on :ps do |str = ''|]
|
60
|
+
# Print String. Prints to the console the first argument.
|
61
|
+
# [on :rc]
|
62
|
+
# Read Character. Reads a single character from the keyboard. This will read control
|
63
|
+
# characters as well as printable characters.
|
64
|
+
# [on :rs]
|
65
|
+
# Read String. Reads characters until the meta character is typed. Primitive editing
|
66
|
+
# characters are available:
|
67
|
+
# [/] Erases the previous character
|
68
|
+
# [@] Erases the entire input string
|
69
|
+
# The editing characters don't change the appearance of input, they just change what the
|
70
|
+
# processor eventually sees. So for example, the following:
|
71
|
+
#
|
72
|
+
# <tt>#(DD@#(PS,Hellp\o World!)'</tt>
|
73
|
+
#
|
74
|
+
# will cause TRAC to print Hello World!
|
75
|
+
# [on :cm do |str|]
|
76
|
+
# Change Meta. Changes the meta character to the first character of the given string.
|
77
|
+
#
|
78
|
+
# ---
|
79
|
+
# <em>Block Commands</em>
|
80
|
+
#
|
81
|
+
# [on :dd do |*names|]
|
82
|
+
# Delete Definitions. Deletes Form definitions that are bound to the given names. Names that are
|
83
|
+
# not bound will be ignored.
|
84
|
+
# [on :da]
|
85
|
+
# Delete all definitions. A synonym for +#(DD,#(LN,(,)))+.
|
86
|
+
# [on :ln do |delimiter = ''|]
|
87
|
+
# List Names. Lists names defined in the current binding using the given delimiter. The delimiter defaults
|
88
|
+
# to the empty string, which means the names will all run together.
|
89
|
+
# [on :sb do |name, *fnames|]
|
90
|
+
# Store Block. Creates a new block with the given name and stores the given forms in it. This will
|
91
|
+
# create a file in the savedir with the name given and an extension of .trl. The file itself will contain
|
92
|
+
# the TRAC commands necessary to recreate the forms given and position their form pointers to the correct place.
|
93
|
+
# [on :fb do |name = ''|]
|
94
|
+
# Fetch Block. Fetches the block with the given name and adds its forms to this environment. This will read the
|
95
|
+
# file given by the form value and execute each line of it as series of TRAC commands. The contents of the block
|
96
|
+
# Form given by the name does not have to use the same directory as the +savedir+, so you can load files from other
|
97
|
+
# directories if you want. However, the Executor that is executing the TRAC commands in the file is using the +savedir+
|
98
|
+
# option, so if the file in question has a #(SB) command, it will use the +savedir+ in the Executor.
|
99
|
+
# [on :eb do |name = ''|]
|
100
|
+
# Erase Block. Deletes block and its corresponding file.
|
101
|
+
#
|
102
|
+
# ---
|
103
|
+
# <em>Math Commands</em>
|
104
|
+
#
|
105
|
+
# [on :bc do |str = ''|]
|
106
|
+
# Bit Complement. Maps the mnemonic <tt>:bc</tt> to Octal.~.
|
107
|
+
#
|
108
|
+
def self.on(sym)
|
109
|
+
table[sym] = Proc.new
|
110
|
+
end
|
111
|
+
|
112
|
+
# Returns empty string. All returns are wrapped in a hash containing
|
113
|
+
# the return value and the force flag.
|
114
|
+
def return_empty
|
115
|
+
{value: '', force: false}
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns value. All returns are wrapped in a hash containing the return
|
119
|
+
# value and the force flag.
|
120
|
+
def return_value(val)
|
121
|
+
{value: val, force: false}
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns value and forces it to the active string.
|
125
|
+
def return_force(val)
|
126
|
+
{value: val, force: true}
|
127
|
+
end
|
128
|
+
|
129
|
+
# Determines the TRAC mnemonic name from the given method name. This is
|
130
|
+
# used to map TRAC names to Form methods.
|
131
|
+
def self.mnemonic(name)
|
132
|
+
name.to_s.split('_').map {|w| w[0]}.join.to_sym
|
133
|
+
end
|
134
|
+
|
135
|
+
# Dispatches command to TRAC procedure. If command received is a TRAC
|
136
|
+
# command, it's looked up in the table and executed. If not, the #(CL)
|
137
|
+
# command is called, and the result is forced to the active string.
|
138
|
+
def dispatch(exp)
|
139
|
+
if Dispatch.table.has_key?(exp.command)
|
140
|
+
self.instance_exec(*exp.trac_args, &Dispatch.table[exp.command])
|
141
|
+
else
|
142
|
+
self.instance_exec(*exp.args, &Dispatch.table[:cl]).merge({force: true})
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Flag for whether trace is on or off. When trace is on,
|
147
|
+
# each time an Expression is parsed for execution, the Executor
|
148
|
+
# will display the Expression and wait for user input. If the
|
149
|
+
# user presses enter, TRAC proceeds as normal. If any other key
|
150
|
+
# is pressed, the Executor is reset.
|
151
|
+
attr_accessor :trace
|
152
|
+
|
153
|
+
# Directory that blocks are read from and written to.
|
154
|
+
attr_accessor :save_dir
|
155
|
+
|
156
|
+
# Meta character to end input. When the Executor is reading from a file,
|
157
|
+
# it will only pass the string on to the Parser after a meta character is
|
158
|
+
# received. Also, the TRAC #(RS) command will only return after a meta
|
159
|
+
# character is pressed.
|
160
|
+
attr_reader :meta
|
161
|
+
|
162
|
+
# Initializes environment for TRAC with a set of options.
|
163
|
+
# Options are:
|
164
|
+
# [bindings] Bindings to use
|
165
|
+
# [trace] Turn trace on or off
|
166
|
+
# [savedir] Directory that blocks are written to and read from.
|
167
|
+
def initialize(**options)
|
168
|
+
@root = options[:bindings] || Bindings.new
|
169
|
+
@trace = options[:trace] || false
|
170
|
+
@savedir = options[:savedir] || './'
|
171
|
+
@meta = "'"
|
172
|
+
end
|
173
|
+
|
174
|
+
# Halt command.
|
175
|
+
on :hl do
|
176
|
+
throw :done
|
177
|
+
end
|
178
|
+
|
179
|
+
# Trace on command.
|
180
|
+
on :tn do
|
181
|
+
@trace = true
|
182
|
+
return_empty
|
183
|
+
end
|
184
|
+
|
185
|
+
# Trace off command.
|
186
|
+
on :tf do
|
187
|
+
@trace = false
|
188
|
+
return_empty
|
189
|
+
end
|
190
|
+
|
191
|
+
# Equal command.
|
192
|
+
on :eq do |str1 = '', str2 = '', t = '', f = ''|
|
193
|
+
return_force(str1 == str2 ? t : f)
|
194
|
+
end
|
195
|
+
|
196
|
+
# Print string command.
|
197
|
+
on :ps do |str = ''|
|
198
|
+
print str
|
199
|
+
return_empty
|
200
|
+
end
|
201
|
+
|
202
|
+
# Read character command.
|
203
|
+
on :rc do
|
204
|
+
return_value(ImmediateRead.new.getch)
|
205
|
+
end
|
206
|
+
|
207
|
+
# Read string command.
|
208
|
+
on :rs do
|
209
|
+
str = ''
|
210
|
+
loop do
|
211
|
+
c = ImmediateRead.new.getch
|
212
|
+
case c
|
213
|
+
when @meta then break
|
214
|
+
when '\\' then str.slice!(-1)
|
215
|
+
when '@' then str = ''
|
216
|
+
else
|
217
|
+
str << c
|
218
|
+
end
|
219
|
+
end
|
220
|
+
return_value(str)
|
221
|
+
end
|
222
|
+
|
223
|
+
# Change meta command.
|
224
|
+
on :cm do |str|
|
225
|
+
@meta = str[0] if str
|
226
|
+
return_empty
|
227
|
+
end
|
228
|
+
|
229
|
+
# Define string command.
|
230
|
+
on :ds do |name = '', value = ''|
|
231
|
+
@root.add([name, Form.new(value)])
|
232
|
+
return_empty
|
233
|
+
end
|
234
|
+
|
235
|
+
# Defines mapping from dispatch command to Form method. The parameters are:
|
236
|
+
# [sym]
|
237
|
+
# Method name in Form to map to.
|
238
|
+
# [type]
|
239
|
+
# One of
|
240
|
+
# * :returning_empty
|
241
|
+
# To return an empty string.
|
242
|
+
# * :returning_value
|
243
|
+
# To return the value returned from the Form method.
|
244
|
+
# * :rescuing_eos
|
245
|
+
# To rescue in case EndOfStringError is raised.
|
246
|
+
# [pos]
|
247
|
+
# Used when type is +:rescuing_eos+. Index of argument
|
248
|
+
# that is returned in case EndOfStringError is raised.
|
249
|
+
#
|
250
|
+
# The following mnemonics are mapped to Form methods:
|
251
|
+
# [:ss]
|
252
|
+
# Segment String. Mapped to Form.segment_string.
|
253
|
+
# [:cr]
|
254
|
+
# Call Return. Mapped to Form.call_return.
|
255
|
+
# [:cl]
|
256
|
+
# Call Lookup. Mapped to Form.call_lookup.
|
257
|
+
# [:cc]
|
258
|
+
# Call Character. Mapped to Form.call_character.
|
259
|
+
# [:cs]
|
260
|
+
# Call Segment. Mapped to Form.call_segment.
|
261
|
+
# [:cn]
|
262
|
+
# Call N Characters. Mapped to Form.call_n.
|
263
|
+
# [:in]
|
264
|
+
# In String. Mapped to Form.in_neutral.
|
265
|
+
def self.dispatch_form(sym, type, pos = -1)
|
266
|
+
table[mnemonic(sym)] = Proc.new do |name = '', *args|
|
267
|
+
f = @root.fetch(name)
|
268
|
+
if !f
|
269
|
+
return_empty
|
270
|
+
else
|
271
|
+
case type
|
272
|
+
when :returning_empty
|
273
|
+
f.send(sym, *args)
|
274
|
+
return_empty
|
275
|
+
when :returning_value
|
276
|
+
return_value(f.send(sym, *args))
|
277
|
+
when :rescuing_eos_at
|
278
|
+
eos_result = args.slice!(pos) || ''
|
279
|
+
begin
|
280
|
+
return_value(f.send(sym, *args))
|
281
|
+
rescue TracLang::Form::EndOfStringError
|
282
|
+
return_force(eos_result)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
dispatch_form(:segment_string, :returning_empty)
|
290
|
+
dispatch_form(:call_return, :returning_empty)
|
291
|
+
dispatch_form(:call_lookup, :returning_value)
|
292
|
+
dispatch_form(:call_character, :rescuing_eos_at, 0)
|
293
|
+
dispatch_form(:call_segment, :rescuing_eos_at, 0)
|
294
|
+
dispatch_form(:call_n, :rescuing_eos_at, 1)
|
295
|
+
dispatch_form(:in_neutral, :rescuing_eos_at, 1)
|
296
|
+
|
297
|
+
# Print form command.
|
298
|
+
on :pf do |name = ''|
|
299
|
+
f = @root.fetch(name)
|
300
|
+
puts f if f
|
301
|
+
return_empty
|
302
|
+
end
|
303
|
+
|
304
|
+
# Delete definitions command.
|
305
|
+
on :dd do |*names|
|
306
|
+
names.each { |name| @root.delete(name) }
|
307
|
+
return_empty
|
308
|
+
end
|
309
|
+
|
310
|
+
# Delete all command.
|
311
|
+
on :da do
|
312
|
+
@root.clear
|
313
|
+
return_empty
|
314
|
+
end
|
315
|
+
|
316
|
+
# List names command.
|
317
|
+
on :ln do |delimiter = ''|
|
318
|
+
return_value(@root.map { |n, v| n }.join(delimiter))
|
319
|
+
end
|
320
|
+
|
321
|
+
# Store block command.
|
322
|
+
on :sb do |name, *fnames|
|
323
|
+
if name
|
324
|
+
to_save = fnames.map { |n| @root.fetch_binding(n) }.compact
|
325
|
+
b = Bindings.new(*to_save)
|
326
|
+
filename = @savedir + name + '.trl'
|
327
|
+
Block.write(filename, b)
|
328
|
+
fnames.each { |n| @root.delete(n) }
|
329
|
+
@root.add([name, Form.new(filename)])
|
330
|
+
end
|
331
|
+
return_empty
|
332
|
+
end
|
333
|
+
|
334
|
+
# Fetch block command.
|
335
|
+
on :fb do |name = ''|
|
336
|
+
f = @root.fetch(name)
|
337
|
+
Block.read(f.value, self) if f
|
338
|
+
return_empty
|
339
|
+
end
|
340
|
+
|
341
|
+
# Erase block command.
|
342
|
+
on :eb do |name = ''|
|
343
|
+
f = @root.fetch(name)
|
344
|
+
Block.delete(f.value) if f
|
345
|
+
@root.delete(name)
|
346
|
+
return_empty
|
347
|
+
end
|
348
|
+
|
349
|
+
# Greater command.
|
350
|
+
on :gr do |num1 = '', num2 = '', t = '', f = ''|
|
351
|
+
unless num1 && num2
|
352
|
+
return_empty
|
353
|
+
else
|
354
|
+
n1 = Decimal.new(num1)
|
355
|
+
n2 = Decimal.new(num2)
|
356
|
+
return_force(n1.value > n2.value ? t : f)
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
# Maps a mnemonic to a Decimal operation. The following are mapped:
|
361
|
+
# [:ad] Add. Maps to Decimal.+.
|
362
|
+
# [:su] Subtract. Maps to Decimal.-.
|
363
|
+
# [:ml] Maps to Decimal.*.
|
364
|
+
# [:dv] Maps to Decimal./. If a ZeroDivisionError is detected, the overflow string is forced to output.
|
365
|
+
def self.dispatch_to_decimal(symbol)
|
366
|
+
Proc.new do |s1 = '', s2 = '', overflow = ''|
|
367
|
+
d1 = Decimal.new(s1)
|
368
|
+
d2 = Decimal.new(s2)
|
369
|
+
begin
|
370
|
+
return_value(d1.send(symbol, d2).to_s)
|
371
|
+
rescue ZeroDivisionError
|
372
|
+
return_force(overflow)
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
on :ad, &dispatch_to_decimal(:+)
|
378
|
+
on :su, &dispatch_to_decimal(:-)
|
379
|
+
on :ml, &dispatch_to_decimal(:*)
|
380
|
+
on :dv, &dispatch_to_decimal(:/)
|
381
|
+
|
382
|
+
# Maps a mnemonic to a Octal operation. The following are mapped:
|
383
|
+
# [:bu] Bit Union. Mapped to Octal.|.
|
384
|
+
# [:bi] Bit Intersection. Mapped to Octal.&.
|
385
|
+
def self.dispatch_to_octal(symbol)
|
386
|
+
Proc.new do |s1 = '', s2 = ''|
|
387
|
+
o1 = Octal.new(s1)
|
388
|
+
o2 = Octal.new(s2)
|
389
|
+
return_value(o1.send(symbol, o2).to_s)
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
on :bu, &dispatch_to_octal(:|)
|
394
|
+
on :bi, &dispatch_to_octal(:&)
|
395
|
+
|
396
|
+
# Bit complement command.
|
397
|
+
on :bc do |str = ''|
|
398
|
+
return_value(Octal.new(str).send(:~).to_s)
|
399
|
+
end
|
400
|
+
|
401
|
+
# Maps a mnemonic to a mixed Octal and Decimal operation. The following are mapped:
|
402
|
+
# [:bs] Bit Shift. Mapped to Octal.shift.
|
403
|
+
# [:br] Bit Rotate. Mapped to Octal.rotate.
|
404
|
+
def self.dispatch_to_mixed(sym)
|
405
|
+
Proc.new do |oct = '', dec = ''|
|
406
|
+
if dec.empty? || oct.empty?
|
407
|
+
return_empty
|
408
|
+
else
|
409
|
+
o1 = Octal.new(oct)
|
410
|
+
d1 = Decimal.new(dec)
|
411
|
+
return_value(o1.send(sym, d1).to_s)
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
on :bs, &dispatch_to_mixed(:shift)
|
417
|
+
on :br, &dispatch_to_mixed(:rotate)
|
418
|
+
|
419
|
+
end
|
420
|
+
|
421
|
+
end
|