-
Notifications
You must be signed in to change notification settings - Fork 0
/
expander.rkt
149 lines (133 loc) · 5.85 KB
/
expander.rkt
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
#lang racket/base
(require bs/opcodes bs/utils bs/structs bs/setup bs/display
racket/string
(for-syntax racket/base)
(for-syntax syntax/parse))
(provide (all-from-out bs/opcodes))
(define-syntax-rule (bs-module-begin expr)
(#%module-begin
(module configure-runtime racket/base
(require bs/setup)
(do-setup!))
(parameterize
([current-output-port (bs-output-port)])
(void expr))))
(provide (rename-out [bs-module-begin #%module-begin])
#%top-interaction
#%app #%datum)
(define (report-invalid-transaction reason)
(displayln (string-append "Invalid: " reason)))
(define (report-valid-transaction)
(displayln "OK: top stack item is a non-zero value"))
;; no op input should take a decimal of total 4 byte
(define FOUR-BYTE-INT-BOUND (/ (expt 256 4) 2))
;; for REPL support, use side effects to remember last sm state
(define SM (s-machine (empty-stack) (empty-stack) #t '()))
(define (handle-args . args)
(for/fold ([sm SM]
#:result
(begin
(set! SM sm) ; save s-machine state
(display-s-machine sm)
(displayln "")
(let ([main-stk (s-machine-main-stk sm)]
[level (s-machine-level sm)]
[tran-state (s-machine-tran-state sm)])
(cond
[(not tran-state)
(report-invalid-transaction "this transaction was being marked as invalid")]
[(not (null? level))
;(displayln level) ;for debug
(report-invalid-transaction "unbalanced OP_IF exist")]
[(stack-empty? main-stk)
(report-invalid-transaction "main stack is empty after execution")]
[else
(let ([top-item (top main-stk)])
(if (= (bytes->integer top-item #t #f) 0)
(report-invalid-transaction "top main stack item is 0 after execution")
(report-valid-transaction)))])))
;; TODO: just for test: show the current state of the stack
)
([op (in-list args)])
#:break (not (s-machine-tran-state sm))
#;(displayln (s-machine-level sm)) ; DEBUG: show current level
(if (or (null? (s-machine-level sm))
(not (car (s-machine-level sm))))
;; when (s-machine-level sm) is empty, execute any command since it's not in an OP_IF block
;; when it's not empty, check (car (s-machine-level sm)): if it's #f then do execute
(begin
;#;
(with-handlers ([exn:fail?
(λ (e)
(let* ([exn-msg (exn-message e)]
[sym-and-msg (string-split exn-msg #rx": ")]
[sym (string->symbol (car sym-and-msg))]
[msg (cadr sym-and-msg)])
(raise-syntax-error sym msg op)))])
((syntax-e op) sm))
;; use below exp for debugging
#;
((syntax-e op) sm))
;; when level list is not emtpy and (top (s-machine-level sm)) => #t
;; which means skip current command until OP_ELSE or OP_ENDIF
(let ([proc (syntax-e op)])
(if (skipping-executable? proc)
(proc sm)
sm)))))
;; =======================
;; Syntax Transformers
;; =======================
(provide bs-program
pushdata-stat
size)
;; Every stat/op will be put back into syntax objects with their source location,
;; for error report.
(define-syntax bs-program
(λ (stx)
(syntax-parse stx
[(bs-program stat/op ...)
#;
(unless (identifier? #'name)
(raise-syntax-error 'form
"expected an identifier for the form"
#'name))
#'(handle-args (datum->syntax #f stat/op #'stat/op) ...)])))
;; size is either specified by hex-string or by exact-nonnegative-integer
(define-syntax-rule (size v)
(if (hex-string? v)
(string->number (string-append "#x" v))
v))
;; A single byte can evaluate to 256 possible results
(define POSSIBLE-COMBINATIONS 256)
;; provide syntax check and error report
;; take care of checking with `size' and `data'
(define-syntax pushdata-stat
(syntax-rules ()
[(pushdata-stat op size data)
(λ (sm)
(let ([old-main-stk (s-machine-main-stk sm)]
[data-size-specifier-upperbound (sub1 (expt POSSIBLE-COMBINATIONS (special-op? 'op)))])
(if (<= 0 size data-size-specifier-upperbound)
(let* ([bytes-to-push (hex-string->bytes data)]
[actual-data-size (bytes-length bytes-to-push)])
(if (= size actual-data-size)
(struct-copy s-machine sm [main-stk (push old-main-stk bytes-to-push)])
(report-pushdata-error size actual-data-size)))
(report-size-specifier-error 'op data-size-specifier-upperbound))))]
[(pushdata-stat size data)
(λ (sm)
(let ([old-main-stk (s-machine-main-stk sm)])
(let* ([bytes-to-push (hex-string->bytes data)]
[actual-data-size (bytes-length bytes-to-push)])
(if (= size actual-data-size)
(struct-copy s-machine sm [main-stk (push old-main-stk bytes-to-push)])
(report-pushdata-error size actual-data-size)))))]))
(define (report-size-specifier-error op upperbound)
(error op
"data size specifier should specify size in between 0 and ~s bytes (both inclusive)"
upperbound))
;; the error below usually do not signal errors but mark transaction state as #f
(define (report-pushdata-error size actual-size)
(error 'OP_PUSHDATA
"specified size (~s bytes) is not equal to the actual size of data being pushed (~s bytes)"
size actual-size))