syntax_tree 5.0.0 → 5.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 +4 -4
- data/.rubocop.yml +51 -0
- data/CHANGELOG.md +24 -1
- data/Gemfile.lock +9 -9
- data/README.md +5 -5
- data/lib/syntax_tree/cli.rb +8 -6
- data/lib/syntax_tree/dsl.rb +1004 -0
- data/lib/syntax_tree/formatter.rb +2 -2
- data/lib/syntax_tree/language_server.rb +2 -0
- data/lib/syntax_tree/node.rb +7 -7
- data/lib/syntax_tree/parser.rb +20 -21
- data/lib/syntax_tree/version.rb +1 -1
- data/lib/syntax_tree/yarv/assembler.rb +459 -0
- data/lib/syntax_tree/yarv/bf.rb +179 -0
- data/lib/syntax_tree/yarv/compiler.rb +2287 -0
- data/lib/syntax_tree/yarv/decompiler.rb +254 -0
- data/lib/syntax_tree/yarv/disassembler.rb +211 -0
- data/lib/syntax_tree/yarv/instruction_sequence.rb +1171 -0
- data/lib/syntax_tree/yarv/instructions.rb +5203 -0
- data/lib/syntax_tree/yarv/legacy.rb +192 -0
- data/lib/syntax_tree/yarv/local_table.rb +89 -0
- data/lib/syntax_tree/yarv.rb +287 -0
- data/lib/syntax_tree.rb +23 -1
- data/syntax_tree.gemspec +1 -1
- metadata +15 -4
@@ -0,0 +1,179 @@
|
|
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(:top, "<compiled>", nil, location)
|
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
|
+
# This is the location of the top instruction sequence, derived from the
|
84
|
+
# source string.
|
85
|
+
def location
|
86
|
+
Location.new(
|
87
|
+
start_line: 1,
|
88
|
+
start_char: 0,
|
89
|
+
start_column: 0,
|
90
|
+
end_line: source.count("\n") + 1,
|
91
|
+
end_char: source.size,
|
92
|
+
end_column: source.size - (source.rindex("\n") || 0) - 1
|
93
|
+
)
|
94
|
+
end
|
95
|
+
|
96
|
+
# $tape[$cursor] += value
|
97
|
+
def change_by(iseq, value)
|
98
|
+
iseq.getglobal(:$tape)
|
99
|
+
iseq.getglobal(:$cursor)
|
100
|
+
|
101
|
+
iseq.getglobal(:$tape)
|
102
|
+
iseq.getglobal(:$cursor)
|
103
|
+
iseq.send(YARV.calldata(:[], 1))
|
104
|
+
|
105
|
+
if value < 0
|
106
|
+
iseq.putobject(-value)
|
107
|
+
iseq.send(YARV.calldata(:-, 1))
|
108
|
+
else
|
109
|
+
iseq.putobject(value)
|
110
|
+
iseq.send(YARV.calldata(:+, 1))
|
111
|
+
end
|
112
|
+
|
113
|
+
iseq.send(YARV.calldata(:[]=, 2))
|
114
|
+
end
|
115
|
+
|
116
|
+
# $cursor += value
|
117
|
+
def shift_by(iseq, value)
|
118
|
+
iseq.getglobal(:$cursor)
|
119
|
+
|
120
|
+
if value < 0
|
121
|
+
iseq.putobject(-value)
|
122
|
+
iseq.send(YARV.calldata(:-, 1))
|
123
|
+
else
|
124
|
+
iseq.putobject(value)
|
125
|
+
iseq.send(YARV.calldata(:+, 1))
|
126
|
+
end
|
127
|
+
|
128
|
+
iseq.setglobal(:$cursor)
|
129
|
+
end
|
130
|
+
|
131
|
+
# $stdout.putc($tape[$cursor].chr)
|
132
|
+
def output_char(iseq)
|
133
|
+
iseq.getglobal(:$stdout)
|
134
|
+
|
135
|
+
iseq.getglobal(:$tape)
|
136
|
+
iseq.getglobal(:$cursor)
|
137
|
+
iseq.send(YARV.calldata(:[], 1))
|
138
|
+
iseq.send(YARV.calldata(:chr))
|
139
|
+
|
140
|
+
iseq.send(YARV.calldata(:putc, 1))
|
141
|
+
end
|
142
|
+
|
143
|
+
# $tape[$cursor] = $stdin.getc.ord
|
144
|
+
def input_char(iseq)
|
145
|
+
iseq.getglobal(:$tape)
|
146
|
+
iseq.getglobal(:$cursor)
|
147
|
+
|
148
|
+
iseq.getglobal(:$stdin)
|
149
|
+
iseq.send(YARV.calldata(:getc))
|
150
|
+
iseq.send(YARV.calldata(:ord))
|
151
|
+
|
152
|
+
iseq.send(YARV.calldata(:[]=, 2))
|
153
|
+
end
|
154
|
+
|
155
|
+
# unless $tape[$cursor] == 0
|
156
|
+
def loop_start(iseq)
|
157
|
+
start_label = iseq.label
|
158
|
+
end_label = iseq.label
|
159
|
+
|
160
|
+
iseq.push(start_label)
|
161
|
+
iseq.getglobal(:$tape)
|
162
|
+
iseq.getglobal(:$cursor)
|
163
|
+
iseq.send(YARV.calldata(:[], 1))
|
164
|
+
|
165
|
+
iseq.putobject(0)
|
166
|
+
iseq.send(YARV.calldata(:==, 1))
|
167
|
+
iseq.branchunless(end_label)
|
168
|
+
|
169
|
+
[start_label, end_label]
|
170
|
+
end
|
171
|
+
|
172
|
+
# Jump back to the start of the loop.
|
173
|
+
def loop_end(iseq, start_label, end_label)
|
174
|
+
iseq.jump(start_label)
|
175
|
+
iseq.push(end_label)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|