-
Notifications
You must be signed in to change notification settings - Fork 256
/
bf.tcl
128 lines (114 loc) · 3.15 KB
/
bf.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
package require Tcl 8.6
namespace eval bf {
::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 pos p} {
foreach x $program {
lassign $x op val
switch -exact -- $op {
INC {
lset tape $pos [expr {[lindex $tape $pos] + $val}]
}
MOVE {
incr pos $val
while {$pos >= [llength $tape]} {
lappend tape 0
}
}
PRINT {
$p print [lindex $tape $pos]
}
LOOP {
while {[lindex $tape $pos] > 0} {
lassign [run $val $tape $pos $p] tape pos
}
}
}
}
return [list $tape $pos]
}
}
proc main {text p} {
lassign [::bf::parse [split $text {}]] program
::bf::run $program 0 0 $p
}
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 (FP)" [pid]]
main $text $p
notify "stop"
if {$quiet} {
puts [format "Output checksum: %d" [$p checksum]]
}
$p destroy
}} {*}$argv