syntax_tree 5.0.1 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SyntaxTree
4
+ module YARV
5
+ # Parses the given source code into a syntax tree, compiles that syntax tree
6
+ # into YARV bytecode.
7
+ class Bf
8
+ attr_reader :source
9
+
10
+ def initialize(source)
11
+ @source = source
12
+ end
13
+
14
+ def compile
15
+ # Set up the top-level instruction sequence that will be returned.
16
+ iseq = InstructionSequence.new("<compiled>", "<compiled>", 1, :top)
17
+
18
+ # Set up the $tape global variable that will hold our state.
19
+ iseq.duphash({ 0 => 0 })
20
+ iseq.setglobal(:$tape)
21
+ iseq.getglobal(:$tape)
22
+ iseq.putobject(0)
23
+ iseq.send(YARV.calldata(:default=, 1))
24
+
25
+ # Set up the $cursor global variable that will hold the current position
26
+ # in the tape.
27
+ iseq.putobject(0)
28
+ iseq.setglobal(:$cursor)
29
+
30
+ stack = []
31
+ source
32
+ .each_char
33
+ .chunk do |char|
34
+ # For each character, we're going to assign a type to it. This
35
+ # allows a couple of optimizations to be made by combining multiple
36
+ # instructions into single instructions, e.g., +++ becomes a single
37
+ # change_by(3) instruction.
38
+ case char
39
+ when "+", "-"
40
+ :change
41
+ when ">", "<"
42
+ :shift
43
+ when "."
44
+ :output
45
+ when ","
46
+ :input
47
+ when "[", "]"
48
+ :loop
49
+ else
50
+ :ignored
51
+ end
52
+ end
53
+ .each do |type, chunk|
54
+ # For each chunk, we're going to emit the appropriate instruction.
55
+ case type
56
+ when :change
57
+ change_by(iseq, chunk.count("+") - chunk.count("-"))
58
+ when :shift
59
+ shift_by(iseq, chunk.count(">") - chunk.count("<"))
60
+ when :output
61
+ chunk.length.times { output_char(iseq) }
62
+ when :input
63
+ chunk.length.times { input_char(iseq) }
64
+ when :loop
65
+ chunk.each do |char|
66
+ case char
67
+ when "["
68
+ stack << loop_start(iseq)
69
+ when "]"
70
+ loop_end(iseq, *stack.pop)
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ iseq.leave
77
+ iseq.compile!
78
+ iseq
79
+ end
80
+
81
+ private
82
+
83
+ # $tape[$cursor] += value
84
+ def change_by(iseq, value)
85
+ iseq.getglobal(:$tape)
86
+ iseq.getglobal(:$cursor)
87
+
88
+ iseq.getglobal(:$tape)
89
+ iseq.getglobal(:$cursor)
90
+ iseq.send(YARV.calldata(:[], 1))
91
+
92
+ if value < 0
93
+ iseq.putobject(-value)
94
+ iseq.send(YARV.calldata(:-, 1))
95
+ else
96
+ iseq.putobject(value)
97
+ iseq.send(YARV.calldata(:+, 1))
98
+ end
99
+
100
+ iseq.send(YARV.calldata(:[]=, 2))
101
+ iseq.pop
102
+ end
103
+
104
+ # $cursor += value
105
+ def shift_by(iseq, value)
106
+ iseq.getglobal(:$cursor)
107
+
108
+ if value < 0
109
+ iseq.putobject(-value)
110
+ iseq.send(YARV.calldata(:-, 1))
111
+ else
112
+ iseq.putobject(value)
113
+ iseq.send(YARV.calldata(:+, 1))
114
+ end
115
+
116
+ iseq.setglobal(:$cursor)
117
+ end
118
+
119
+ # $stdout.putc($tape[$cursor].chr)
120
+ def output_char(iseq)
121
+ iseq.getglobal(:$stdout)
122
+
123
+ iseq.getglobal(:$tape)
124
+ iseq.getglobal(:$cursor)
125
+ iseq.send(YARV.calldata(:[], 1))
126
+ iseq.send(YARV.calldata(:chr))
127
+
128
+ iseq.send(YARV.calldata(:putc, 1))
129
+ iseq.pop
130
+ end
131
+
132
+ # $tape[$cursor] = $stdin.getc.ord
133
+ def input_char(iseq)
134
+ iseq.getglobal(:$tape)
135
+ iseq.getglobal(:$cursor)
136
+
137
+ iseq.getglobal(:$stdin)
138
+ iseq.send(YARV.calldata(:getc))
139
+ iseq.send(YARV.calldata(:ord))
140
+
141
+ iseq.send(YARV.calldata(:[]=, 2))
142
+ iseq.pop
143
+ end
144
+
145
+ # unless $tape[$cursor] == 0
146
+ def loop_start(iseq)
147
+ start_label = iseq.label
148
+ end_label = iseq.label
149
+
150
+ iseq.push(start_label)
151
+ iseq.getglobal(:$tape)
152
+ iseq.getglobal(:$cursor)
153
+ iseq.send(YARV.calldata(:[], 1))
154
+
155
+ iseq.putobject(0)
156
+ iseq.send(YARV.calldata(:==, 1))
157
+ iseq.branchif(end_label)
158
+
159
+ [start_label, end_label]
160
+ end
161
+
162
+ # Jump back to the start of the loop.
163
+ def loop_end(iseq, start_label, end_label)
164
+ iseq.getglobal(:$tape)
165
+ iseq.getglobal(:$cursor)
166
+ iseq.send(YARV.calldata(:[], 1))
167
+
168
+ iseq.putobject(0)
169
+ iseq.send(YARV.calldata(:==, 1))
170
+ iseq.branchunless(start_label)
171
+
172
+ iseq.push(end_label)
173
+ end
174
+ end
175
+ end
176
+ end