trac_lang 0.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/.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
|