11require "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
106110end
107111
112+ # TODO: Fix
108113if __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