-
Notifications
You must be signed in to change notification settings - Fork 255
/
bf_oo.tcl
149 lines (131 loc) · 3.45 KB
/
bf_oo.tcl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package require Tcl 8.6
namespace eval bf {} {
::oo::class create Tape {
variable tape pos
constructor {} {
set tape 0
set pos 0
}
method current {} {
return [lindex $tape $pos]
}
method inc x {
lset tape $pos [expr {[lindex $tape $pos] + $x}]
}
method move x {
incr pos $x
while {$pos >= [llength $tape]} {
lappend tape 0
}
}
}
::oo::class create Printer {
variable sum1 sum2 quiet
constructor q {
set sum1 0
set sum2 0
set quiet $q
}
method print n {
if {$quiet} {
set sum1 [expr ($sum1 + $n) % 255]
set sum2 [expr ($sum2 + $sum1) % 255]
} else {
puts -nonewline [format %c $n]
flush stdout
}
}
method checksum {} {
return [expr ($sum2 << 8) | $sum1]
}
}
proc parse source {
set res {}
while 1 {
set c [lindex $source 0]
if {$c eq {}} break
set source [lrange $source 1 end]
switch -exact -- $c {
+ { lappend res [list INC 1] }
- { lappend res [list INC -1] }
> { lappend res [list MOVE 1] }
< { lappend res [list MOVE -1] }
. { lappend res [list PRINT {}] }
\[ {
lassign [parse $source] loop_code source
lappend res [list LOOP $loop_code]
}
\] { break }
default {}
}
}
return [list $res $source]
}
proc run {program tape p} {
foreach x $program {
lassign $x op val
switch -exact -- $op {
INC {
$tape inc $val
}
MOVE {
$tape move $val
}
PRINT {
$p print [$tape current]
}
LOOP {
while {[$tape current] > 0} {
run $val $tape $p
}
}
}
}
}
}
proc main {text p} {
lassign [::bf::parse [split $text {}]] program
set tape [::bf::Tape new]
::bf::run $program $tape $p
$tape destroy
}
proc notify msg {
catch {
set sock [socket "localhost" 9001]
puts $sock $msg
close $sock
}
}
proc verify {} {
set text {++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>
---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.}
set p_left [::bf::Printer new 1]
main $text $p_left
set left [$p_left checksum]
$p_left destroy
set p_right [::bf::Printer new 1]
foreach c [split "Hello World!\n" ""] {
$p_right print [scan $c %c]
}
set right [$p_right checksum]
$p_right destroy
if {$left != $right} {
puts stderr [format "%d != %d" $left $right]
exit 1
}
}
apply {{filename} {
verify
set f [open $filename]
set text [read $f]
close $f
set quiet [info exists ::env(QUIET)]
set p [::bf::Printer new $quiet]
notify [format "%s\t%d" "Tcl (OOP)" [pid]]
main $text $p
notify "stop"
if {$quiet} {
puts [format "Output checksum: %d" [$p checksum]]
}
$p destroy
}} {*}$argv