Skip to content

Commit 8b073c5

Browse files
committed
Warp Ruby parser in module
1 parent 875d017 commit 8b073c5

File tree

1 file changed

+93
-88
lines changed

1 file changed

+93
-88
lines changed

ports-r/parser.rb

Lines changed: 93 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,115 @@
11
require "json"
22

3-
def tokenize(expression)
4-
pattern = /\s*(,@|[('`,)]|"(?:\\.|[^\\"])*"|;.*|[^\s('"`;,)]*)(.*)/
5-
tokens = []
3+
module Parser
4+
def tokenize(expression)
5+
pattern = /\s*(,@|[('`,)]|"(?:\\.|[^\\"])*"|;.*|[^\s('"`;,)]*)(.*)/
6+
tokens = []
67

7-
expression.split("\n").each do |line|
8-
part = line
9-
while match = part.match(pattern) and match[0] != ""
10-
token = match[1]
11-
if token && token != "" && !token.start_with?(";")
12-
tokens << token
8+
expression.split("\n").each do |line|
9+
part = line
10+
while (match = part.match(pattern)) && match[0] != ""
11+
token = match[1]
12+
if token && token != "" && !token.start_with?(";")
13+
tokens << token
14+
end
15+
part = match[2]
1316
end
14-
part = match[2]
1517
end
18+
tokens
1619
end
17-
tokens
18-
end
1920

20-
def parse_tokens(tokens)
21-
return [] if tokens.empty?
21+
def parse_tokens(tokens)
22+
return [] if tokens.empty?
2223

23-
token = tokens.shift
24-
if token == "("
25-
list = []
26-
while tokens[0] != ")"
27-
list << parse_tokens(tokens)
24+
token = tokens.shift
25+
if token == "("
26+
list = []
27+
while tokens[0] != ")"
28+
list << parse_tokens(tokens)
29+
end
30+
tokens.shift # Remove ')'
31+
list
32+
elsif token == ")"
33+
raise "Unexpected ')'"
34+
elsif token == "'"
35+
[:quote, parse_tokens(tokens)]
36+
elsif token == "`"
37+
[:quasiquote, parse_tokens(tokens)]
38+
elsif token == ","
39+
[:unquote, parse_tokens(tokens)]
40+
elsif token == ",@"
41+
[:"unquote-splicing", parse_tokens(tokens)]
42+
else
43+
parse_atom(token)
2844
end
29-
tokens.shift # Remove ')'
30-
list
31-
elsif token == ")"
32-
raise "Unexpected ')'"
33-
elsif token == "'"
34-
[:quote, parse_tokens(tokens)]
35-
elsif token == "`"
36-
[:quasiquote, parse_tokens(tokens)]
37-
elsif token == ","
38-
[:unquote, parse_tokens(tokens)]
39-
elsif token == ",@"
40-
[:"unquote-splicing", parse_tokens(tokens)]
41-
else
42-
parse_atom(token)
4345
end
44-
end
4546

46-
def parse_atom(token)
47-
lower_token = token.downcase
48-
if lower_token == "#t" || lower_token == "#true"
49-
return true
50-
end
51-
if lower_token == "#f" || lower_token == "#false"
52-
return false
53-
end
54-
if token[0] == '"'
55-
raw_string = token[1..-2]
56-
return raw_string.gsub("\\n", "\n")
57-
.gsub("\\r", "\r")
58-
.gsub("\\t", "\t")
59-
end
47+
def parse_atom(token)
48+
lower_token = token.downcase
49+
if lower_token == "#t" || lower_token == "#true"
50+
return true
51+
end
52+
if lower_token == "#f" || lower_token == "#false"
53+
return false
54+
end
55+
if token[0] == '"'
56+
raw_string = token[1..-2]
57+
return raw_string.gsub("\\n", "\n")
58+
.gsub("\\r", "\r")
59+
.gsub("\\t", "\t")
60+
end
6061

61-
# Try to parse as integer
62-
begin
63-
integer = Integer(token)
64-
return integer
65-
rescue ArgumentError
66-
# Not an integer
67-
end
62+
# Try to parse as integer
63+
begin
64+
integer = Integer(token)
65+
return integer
66+
rescue ArgumentError
67+
# Not an integer
68+
end
6869

69-
# Try to parse as float
70-
begin
71-
float = Float(token)
72-
return float
73-
rescue ArgumentError
74-
# Not a number
75-
end
70+
# Try to parse as float
71+
begin
72+
float = Float(token)
73+
return float
74+
rescue ArgumentError
75+
# Not a number
76+
end
7677

77-
token.to_sym # Use Ruby's built-in symbols
78-
end
78+
token.to_sym # Use Ruby's built-in symbols
79+
end
7980

80-
def parse_without_expand(input_string)
81-
tokens = tokenize(input_string)
82-
parse_tokens(tokens)
83-
end
81+
def parse_string(input_string)
82+
tokens = tokenize(input_string)
83+
parse_tokens(tokens)
84+
end
8485

85-
def matches(structure, target)
86-
if target.is_a?(Array)
87-
return false unless structure.is_a?(Array)
88-
return false unless structure.length == target.length
86+
def matches(structure, target)
87+
if target.is_a?(Array)
88+
return false unless structure.is_a?(Array)
89+
return false unless structure.length == target.length
8990

90-
result = true
91-
target.each_with_index do |t, i|
92-
result &&= matches(structure[i], t)
91+
result = true
92+
target.each_with_index do |t, i|
93+
result &&= matches(structure[i], t)
94+
end
95+
result
96+
elsif target == "Boolean"
97+
structure == true || structure == false
98+
elsif target == "String"
99+
structure.is_a?(String)
100+
elsif target == "Character"
101+
structure.is_a?(String) && structure.length == 1
102+
elsif target == "Symbol"
103+
structure.is_a?(Symbol) # Check against Ruby's built-in Symbol class
104+
elsif target == "Number"
105+
structure.is_a?(Numeric)
93106
end
94-
result
95-
elsif target == "Boolean"
96-
structure == true || structure == false
97-
elsif target == "String"
98-
structure.is_a?(String)
99-
elsif target == "Character"
100-
structure.is_a?(String) && structure.length == 1
101-
elsif target == "Symbol"
102-
structure.is_a?(Symbol) # Check against Ruby's built-in Symbol class
103-
elsif target == "Number"
104-
structure.is_a?(Numeric)
105107
end
108+
109+
module_function :parse_string, :tokenize, :parse_tokens, :parse_atom, :matches
106110
end
107111

112+
# TODO: Fix
108113
if __FILE__ == $PROGRAM_NAME
109114
test_table = JSON.parse(File.read("ports/syntax-tests.json"))
110115

@@ -115,8 +120,8 @@ def matches(structure, target)
115120
next
116121
end
117122

118-
parse_result = parse_without_expand(entry[0])
119-
if matches(parse_result, entry[1])
123+
parse_result = Parser.parse_string(entry[0])
124+
if Parser.matches(parse_result, entry[1])
120125
puts "✅: #{entry}"
121126
else
122127
puts "❌: #{entry} got #{parse_result.inspect} instead"

0 commit comments

Comments
 (0)