lisp-interpreter 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.
@@ -0,0 +1,51 @@
1
+ # redefine method in Object class
2
+ class Object
3
+ def number?
4
+ to_f.to_s == to_s || to_i.to_s == to_s
5
+ end
6
+
7
+ def to_num
8
+ return to_f if to_f.to_s == to_s
9
+ return to_i if to_i.to_s == to_s
10
+ end
11
+
12
+ def character?
13
+ (start_with? '#\\') && (('a'..'z').to_a.include? self[2]) && size == 3
14
+ end
15
+
16
+ def string?
17
+ return false unless self.class == String
18
+ (start_with? '"') && (end_with? '"') && (size != 1)
19
+ end
20
+
21
+ def list?
22
+ return false if size < 3
23
+ check_for_list
24
+ end
25
+
26
+ def pair?
27
+ res = object_split if is_a? String
28
+ res = to_a if is_a? Array
29
+ return true if res[-3] == '.'
30
+ list? && !res[2..-2].empty?
31
+ end
32
+
33
+ def quote?
34
+ return true if start_with? '\''
35
+ false
36
+ end
37
+
38
+ private
39
+
40
+ def object_split
41
+ result = to_s.split(/(\(|\)|\.)|\ /)
42
+ result.delete('')
43
+ result
44
+ end
45
+
46
+ def check_for_list
47
+ res = to_a if is_a? Array
48
+ res = object_split if is_a? String
49
+ res[0..1].join == '\'(' && res[-1] == ')' && res[-3] != '.'
50
+ end
51
+ end
@@ -0,0 +1,72 @@
1
+ require_relative 'errors'
2
+ require_relative 'validator'
3
+ require_relative 'tokenizer'
4
+
5
+ # Environment type
6
+ module Environment
7
+ TEST = 1
8
+ PROD = 2
9
+ end
10
+
11
+ # Parser is used to validate the user input and parse it to the tokenizer
12
+ class Parser
13
+ include ErrorMessages
14
+ include Validator
15
+ include Environment
16
+
17
+ def initialize(env_type = Environment::TEST)
18
+ @ENV_TYPE = env_type
19
+ @tokenizer = Tokenizer.new
20
+ end
21
+
22
+ def run
23
+ loop do
24
+ print 'zakichan> ' if @ENV_TYPE == Environment::PROD
25
+ token = ''
26
+ until (validate_token token).nil? && token != ''
27
+ crr_input = STDIN.gets.chomp
28
+ token << crr_input
29
+ break if crr_input == ''
30
+ end
31
+ parse token
32
+ end
33
+ end
34
+
35
+ def split_token(token)
36
+ result = []
37
+ token.split(/\s+(?=(?:[^"]*"[^"]*")*[^"]*$)/).each do |t|
38
+ if !t.string? && (t.include?('(') || t.include?(')'))
39
+ t.to_s.split(/(\(|\))/).each { |p| result << p }
40
+ else
41
+ result << t
42
+ end
43
+ end
44
+ result
45
+ end
46
+
47
+ def parse(token)
48
+ token_error = validate_token token
49
+ result =
50
+ if token_error.nil?
51
+ @tokenizer.tokenize split_token token
52
+ else
53
+ token_error
54
+ end
55
+ print_result result unless result.to_s.empty?
56
+ end
57
+
58
+ def validate_token(token)
59
+ if !balanced_brackets? token
60
+ unbalanced_brackets_error
61
+ elsif !balanced_quotes? token
62
+ unbalanced_quotes_error
63
+ end
64
+ end
65
+
66
+ def print_result(result)
67
+ to_remove = result.to_s.list? || result.to_s.pair? || result.to_s.quote?
68
+ result = result.delete('\'') if to_remove
69
+ puts result if @ENV_TYPE == Environment::PROD
70
+ result
71
+ end
72
+ end
@@ -0,0 +1,4 @@
1
+ require_relative 'parser'
2
+
3
+ parser = Parser.new Environment::PROD
4
+ parser.run
@@ -0,0 +1,131 @@
1
+ # Helper functions for SchemeStrings
2
+ module SchemeStringsHelper
3
+ def substring_builder(str, from, to)
4
+ result = (str[1..-2])[from..(to.nil? ? -1 : to - 1)]
5
+ return '""' if result.nil?
6
+ '"' + result + '"'
7
+ end
8
+
9
+ def find_delimeter(other)
10
+ return ' ' if other.nil?
11
+ other[1..-2]
12
+ end
13
+
14
+ def build_as_string_helper(other, idx)
15
+ value = other[0..idx].join(' ').gsub('( ', '(').gsub(' )', ')')
16
+ [value, other[idx + 1..-1]]
17
+ end
18
+
19
+ def build_next_value_as_string(other)
20
+ idx = find_idx_for_list other
21
+ if other[0] == '('
22
+ build_as_string_helper other, idx
23
+ elsif other[0..1].join == '\'('
24
+ [(get_raw_value other[0..idx]), other[idx + 1..-1]]
25
+ else
26
+ [other[0], other[1..-1]]
27
+ end
28
+ end
29
+
30
+ def build_character(char)
31
+ '#\\' + (char == ' ' ? 'space' : char)
32
+ end
33
+
34
+ def remove_carriage(str)
35
+ str = str[1..-2]
36
+ str.gsub('\n', '').gsub('\r', '').gsub('\t', '').strip.squeeze(' ')
37
+ end
38
+
39
+ def arg_function_validator(other, vars = 1)
40
+ raise 'Incorrect number of arguments' if other.size != vars
41
+ result = other[0..vars - 1].all? { |v| check_for_string v }
42
+ raise 'Invalid data type' unless result
43
+ result
44
+ end
45
+
46
+ def string_join_helper(other, dilimeter)
47
+ values = split_list_as_string other.to_s
48
+ delim_result = find_delimeter dilimeter
49
+ '"' + (values.join delim_result) + '"'
50
+ end
51
+ end
52
+
53
+ # Scheme numbers module
54
+ module SchemeStrings
55
+ include SchemeStringsHelper
56
+ def substring(other)
57
+ raise 'Incorrect number of arguments' unless other.size.between? 2, 3
58
+ str, from, to = other
59
+ arg_function_validator [str]
60
+ valid = (check_for_number from) && (to.nil? || (check_for_number to))
61
+ raise 'Incorrect parameter type' unless valid
62
+ substring_builder str, from.to_num, to.to_num
63
+ end
64
+
65
+ def string?(other)
66
+ raise 'Incorrect number of arguments' if other.size != 1
67
+ result = check_for_string other[0].to_s
68
+ result ? '#t' : '#f'
69
+ end
70
+
71
+ def strlen(other)
72
+ arg_function_validator other
73
+ other[0][1..-2].length
74
+ end
75
+
76
+ def strupcase(other)
77
+ arg_function_validator other
78
+ other[0].upcase
79
+ end
80
+
81
+ def strdowncase(other)
82
+ arg_function_validator other
83
+ other[0].downcase
84
+ end
85
+
86
+ def strcontains(other)
87
+ arg_function_validator other, 2
88
+ result = other[0][1..-2].include? other[1][1..-2]
89
+ result ? '#t' : '#f'
90
+ end
91
+
92
+ def strsplit(other)
93
+ arg_function_validator other
94
+ str = remove_carriage other[0]
95
+ result = str.split(' ').map { |s| '"' + s + '"' }
96
+ build_list result
97
+ end
98
+
99
+ def strlist(other)
100
+ arg_function_validator other
101
+ result = other[0][1..-2].chars.map { |c| build_character c }
102
+ build_list result
103
+ end
104
+
105
+ def strreplace(other)
106
+ arg_function_validator other, 3
107
+ str, to_replace, replace_with = other.map { |t| t[1..-2] }
108
+ '"' + (str.gsub to_replace, replace_with) + '"'
109
+ end
110
+
111
+ def strprefix(other)
112
+ arg_function_validator other, 2
113
+ str, to_check = other.map { |t| t[1..-2] }
114
+ result = str.start_with? to_check
115
+ result ? '#t' : '#f'
116
+ end
117
+
118
+ def strsufix(other)
119
+ arg_function_validator other, 2
120
+ str, to_check = other.map { |t| t[1..-2] }
121
+ result = str.end_with? to_check
122
+ result ? '#t' : '#f'
123
+ end
124
+
125
+ def strjoin(other)
126
+ raise 'Incorrect number of arguments' unless other.size.between? 1, 2
127
+ raise 'Invalid data type' unless other[0].to_s.list?
128
+ arg_function_validator [other[1]] if other.size == 2
129
+ string_join_helper other[0], other[1]
130
+ end
131
+ end
@@ -0,0 +1,185 @@
1
+ require_relative 'object'
2
+ require_relative 'errors'
3
+ require_relative 'value_finder'
4
+ require_relative 'checker'
5
+ require_relative 'validator'
6
+ require_relative 'numbers'
7
+ require_relative 'strings'
8
+ require_relative 'boolean'
9
+ require_relative 'list'
10
+ require_relative 'functional'
11
+
12
+ # Tokenizer helper
13
+ module TokenizerHelper
14
+ def initialize
15
+ @other = []
16
+ @procs = {}
17
+ @do_not_calculate = init_do_not_calculate_fn
18
+ @reserved = init_reserved_fn
19
+ set_reserved_keywords
20
+ @functions = init_functions
21
+ init_predefined.each { |f| @functions[f] = f }
22
+ end
23
+
24
+ def reset
25
+ @other = []
26
+ end
27
+
28
+ def init_do_not_calculate_fn
29
+ %w[
30
+ foldl foldr map filter
31
+ if apply numerator denominator
32
+ lambda compose define
33
+ ]
34
+ end
35
+
36
+ def init_functions
37
+ {
38
+ 'string-downcase' => 'strdowncase', 'string-upcase' => 'strupcase',
39
+ 'string-contains?' => 'strcontains', 'string-length' => 'strlen',
40
+ 'string->list' => 'strlist', 'string-split' => 'strsplit',
41
+ 'string-sufix?' => 'strsufix', 'string-prefix?' => 'strprefix',
42
+ 'string-replace' => 'strreplace', 'string-join' => 'strjoin',
43
+ 'list-ref' => 'listref', 'list-tail' => 'listtail'
44
+ }
45
+ end
46
+
47
+ def init_predefined
48
+ %w[
49
+ define not equal? if quotient remainder modulo numerator denominator
50
+ min max sub1 add1 abs string? substring null? cons null list car
51
+ cdr list? pair? length reverse remove shuffle map foldl foldr filter
52
+ member lambda apply compose
53
+ ]
54
+ end
55
+
56
+ def init_reserved_fn
57
+ {
58
+ 'null' => '\'()'
59
+ }
60
+ end
61
+
62
+ def set_reserved_keywords
63
+ @reserved.each do |key, value|
64
+ instance_variable_set("@#{key}", value)
65
+ end
66
+ end
67
+
68
+ def set_var_helper(var, value)
69
+ valid = (valid_var value.to_s) || (value.is_a? Proc)
70
+ raise 'Invalid parameter' unless valid
71
+ if value.is_a? Proc
72
+ remove_instance_variable("@#{var}") if check_instance_var var
73
+ @procs[var] = value if value.is_a? Proc
74
+ else
75
+ @procs.delete var
76
+ set_var var, value
77
+ end
78
+ end
79
+
80
+ def set_var(var, value)
81
+ raise 'Cannot predefine reserved keyword' if @reserved.key? var
82
+ instance_variable_set("@#{var}", value)
83
+ end
84
+
85
+ def get_var(var)
86
+ check = check_instance_var var
87
+ return instance_variable_get("@#{var}") if check
88
+ val = (predefined_method_caller [var])
89
+ return val unless val.nil?
90
+ valid = valid_var var
91
+ valid ? var : (raise 'Invalid data type')
92
+ end
93
+ end
94
+
95
+ # Tokenizer class
96
+ class Tokenizer
97
+ include TokenizerHelper
98
+ include ValueFinder
99
+ include SchemeChecker
100
+ include Validator
101
+ include SchemeNumbers
102
+ include SchemeStrings
103
+ include SchemeBooleans
104
+ include SchemeLists
105
+ include FunctionalScheme
106
+
107
+ def tokenize(token)
108
+ reset
109
+ token.delete('')
110
+ @other = token
111
+ begin
112
+ calc_input_val @other
113
+ rescue ZeroDivisionError, RuntimeError => e
114
+ e.message
115
+ end
116
+ end
117
+
118
+ def check_car_cdr(arr)
119
+ result = arr[1].match(/c[ad]{2,}r/)
120
+ raise 'No procedure found' if result.nil?
121
+ car_cdr_infinite arr
122
+ end
123
+
124
+ def calc_input_val(arr)
125
+ get_raw = (arr.is_a? Array) && arr.size > 1 && arr[0..1].join != '\'('
126
+ return get_raw_value arr unless get_raw
127
+ m_name = predefined_method_caller arr
128
+ return check_car_cdr arr if m_name.nil?
129
+ call_predefined_method m_name, arr
130
+ end
131
+
132
+ def special_check_proc(m_name, arr)
133
+ if arr[0..1].join == '(('
134
+ idx = find_bracket_idx arr, 1
135
+ func, = valid_function arr[1..idx]
136
+ values = find_all_values arr[idx + 1..-2]
137
+ func.call(*values)
138
+ else
139
+ m_name.call(*arr[2..-2])
140
+ end
141
+ end
142
+
143
+ def call_predefined_method(m_name, arr)
144
+ return special_check_proc m_name, arr if m_name.is_a? Proc
145
+ if @do_not_calculate.include? m_name
146
+ send m_name.to_s, arr[2..-2]
147
+ elsif !m_name.nil?
148
+ values = find_all_values arr[2..-2]
149
+ send m_name.to_s, values
150
+ end
151
+ end
152
+
153
+ def predefined_method_caller_helper(m_name, operations)
154
+ return m_name if m_name.is_a? Proc
155
+ return @procs[m_name] if @procs.key? m_name
156
+ return m_name if operations.include? m_name
157
+ return @functions[m_name] if @functions.key? m_name
158
+ m_name if @functions.value? m_name
159
+ end
160
+
161
+ def method_caller_checker(token, operations)
162
+ !token.to_s.match(/[[:alpha:]]/).nil? || (operations.include? token.to_s)
163
+ end
164
+
165
+ def predefined_method_caller(arr)
166
+ operations = ['+', '-', '/', '*', '<', '<=', '>', '>=']
167
+ m_name =
168
+ arr.each do |t|
169
+ break t if t.is_a? Proc
170
+ break t if method_caller_checker t, operations
171
+ break t unless t.match(/[[:digit:]]/).nil?
172
+ end
173
+ predefined_method_caller_helper m_name, operations
174
+ end
175
+
176
+ def get_raw_value(token)
177
+ if token.pair? || token.list?
178
+ build_list no_eval_list token[2..-2]
179
+ else
180
+ return if token.empty?
181
+ token = token.join('') if token.is_a? Array
182
+ get_var token.to_s
183
+ end
184
+ end
185
+ end