1 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2 ;; and now
for something completely different
...
5 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
6 ;; Z80 Assembler
: expression parser
7 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
10 expression evaluator is done as a compiler
to Forth code
.
11 any math expression is compiled
to executable Forth code
. on executing
,
12 this code creates label references
, and THROWs on undefined label
.
16 vocab
-if-none asm
-expr
21 vocab
-if-none asm
-funcs
24 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
25 ;; error codes
for "throw"
28 0 value err
-label
-c1str
31 def
: error
-undefined
-label
32 def
: error
-division
-by
-0
33 def
: error
-invalid
-shift
36 : error
-undefined
-label?
( code
-- bool
) error
-undefined
-label
= ;
38 : error
-message
( code
-- addr count
)
40 error
-undefined
-label of
" undefined label" endof
41 error
-division
-by
-0 of
" division by 0" endof
42 error
-invalid
-shift of
" invalid shift" endof
43 otherwise drop
" unknown error"
48 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
49 ;; generate code
for expression evaluation
(low level
)
53 : (run
-forth
-at
-addr
) ( ... addr
-- ... )
54 [ ' forth:(lit) , here 0 , ] compiler:(branch-addr!)
55 [ ' forth
:(branch
) , here
0 , swap
! ]
58 \ debug
:decompile
(run
-forth
-at
-addr
)
60 -1 value bc
-buffer
-start
61 -1 value bc
-buffer
-end
63 ;; this references label only once
64 ;; uses self
-modifying code
65 : (ref
-label
) ( addr count
-- addr count
)
66 2dup r
> dup @
( addr count ip refed?
)
68 else nrot asm
-labels
:ref true over
!
70 ; compiler
:(warg
-lit
) compiler
:set
-warg
72 ;; compiled
to generated expression code
73 : (get
-label
) ( addr count
-- value
)
74 asm
-labels
:get ifnot error
-undefined
-label throw
75 else ( ." LABEL: " dup
. cr
)
79 : (div
) ( a b
-- ) dup ifnot error
-division
-by
-0 throw
endif / ;
80 : (mod
) ( a b
-- ) dup ifnot error
-division
-by
-0 throw
endif mod
;
82 : (sal
) ( a count
-- )
83 0 32 within ifnot error
-invalid
-shift throw
endif
87 : (sar
) ( a count
-- )
88 0 32 within ifnot error
-invalid
-shift throw
endif
93 : bc
-init
( -- ) here
to bc
-buffer
-start
;
96 bc
-buffer
-start
0< " cannot undo bc -- nothing to do" ?error
97 bc
-buffer
-start forth
:(dp
) !
102 : bc
-run
( addr
-- result TRUE
/ errorcode FALSE
)
103 ['] (run-forth-at-addr) catch ?dup if nip ( drop `addr` ) false
107 : bc-do-as-compiler: ( cfa -- ) \ name word
108 create parse-skip-comments -find-required ,
110 state @ >r state 1! @execute r> state !
113 bc-do-as-compiler: bc-, ( n -- ) forth:,
114 bc-do-as-compiler: bc-compile, ( cfa -- ) compile,
115 bc-do-as-compiler: bc-compile ( -- ) compile immediate
116 bc-do-as-compiler: bc-emit-lit ( n -- ) literal
117 bc-do-as-compiler: bc-emit-strlit ( n -- ) strliteral
119 : bc-emit-label ( addr count -- )
120 2dup 1 = swap c@ [char] $ = and if
121 2drop asm-emit:pc$ bc-emit-lit
123 asm-labels:fix-prefix bc-emit-strlit
124 bc-compile (ref-label) false bc-,
125 bc-compile (get-label)
130 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
131 ;; generate code for expression evaluation (parser)
137 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
138 ;; terminal and unaries
143 tk-num of tok-num bc-emit-lit next-token endof
144 tk-id of ;; label or function
145 token vocid: asm-funcs find-word-in-voc ( cfa TRUE / FALSE )
146 if next-token execute
147 else token bc-emit-label next-token
151 tok-num 0< " unexpected string" ?error
152 tok-num bc-emit-lit next-token
155 endcr space token xtype ." ? -- wut?" cr " unknown term" error
160 tok-type tk-punct = if
163 next-token (parse-expr)
164 tok-type tk-punct = tok-num [char] ) = and " unbalanced parens" ?not-error
182 otherwise " invalid expression (term expected)" error
189 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
190 ;; define binary operators table
193 : def-op ( prio -- prio ) \ token doer
195 parse-name dup 1 2 bounds? " invalid token" ?not-error
196 1 = if c@ else w@ endif , ;; put token code
197 -find-required , ;; doer CFA
200 : -next-prio- ( prio -- prio+1 ) 1+ ;
202 1 ;; current priority
231 def-op && forth:(0branch)
233 def-op || forth:(tbranch)
237 constant min-binary-prio
238 \ min-binary-prio 2 <> " shit!" ?error
241 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
242 ;; table-driven expression parser
245 : (xbranch?) ( opc -- bool )
246 dup ['] forth
:(0branch
) = swap
['] forth:(tbranch) = or
249 ;; find operator using the given priority and the current token
250 : (find-operator) ( prio -- opcode TRUE / FALSE )
251 tok-type tk-punct = if
255 dup cell+ @ tok-num = if
256 rdrop 2 +cells @ true exit
267 dup min-binary-prio < if drop unary
269 dup 1- recurse ;; left operand
271 0 >r ;; save jump chain start to rstack for short-circuit logicals
275 next-token ;; skip operator
277 ;; logic op: jump over the following code
279 bc-compile, r> compiler:(chain-j>) >r
283 over 1- recurse bc-compile,
286 r> compiler:(resolve-j>)
295 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
299 : (setup-lexer) ( -- )
300 false to asm-lexer:line-start
303 : (restore-lexer) ( -- )
306 ;; reset expression code buffer to HERE, parse expression, generate code
309 bc-init (parse-expr) bc-compile forth:(exit)
310 here to bc-buffer-end
315 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
316 ;; expression range checkers
319 : (expr-check-byte) ( value -- ) -128 256 within " 8-bit value out of range" ?not-error ;
320 : (expr-check-addr) ( value -- ) 0 65536 within " address out of range" ?not-error ;
321 : (expr-check-ubyte) ( value -- ) 0 256 within " unsigned 8-bit value out of range" ?not-error ;
322 : (expr-check-word) ( value -- ) -32768 65536 within " 16-bit value out of range" ?not-error ;
323 : (expr-check-im) ( value -- ) 0 3 within " invalid IM number" ?not-error ;
324 : (expr-check-rel8) ( value -- ) -128 128 within " relative jump too long" ?not-error ;
325 : (expr-check-bit) ( value -- ) 0 8 within " invalid bit number" ?not-error ;
326 : (expr-check-disp+) ( value -- ) -128 128 within " displacement out of range" ?not-error ;
327 : (expr-check-disp-) ( value -- ) negate -128 128 within " displacement out of range" ?not-error ;
328 : (expr-check-rst) ( value -- ) dup 0 0x38 bounds? swap 0x03 and 0= and " invalid RST address" ?not-error ;
331 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
332 ;; fixers for postponed expressions
338 fix-value dup (expr-check-addr)
343 fix-value dup (expr-check-word)
348 fix-value dup (expr-check-word)
349 fix-disp asm-emit:rw!
353 fix-value dup (expr-check-byte)
358 fix-value dup (expr-check-ubyte)
363 fix-value dup (expr-check-disp+)
368 fix-value negate dup (expr-check-disp+)
373 fix-value fix-pc 1+ - dup (expr-check-rel8)
378 fix-value dup (expr-check-im)
379 asm-emit:(im) fix-disp asm-emit:c!
383 fix-value dup (expr-check-rst)
384 fix-disp asm-emit:rst-c!
388 fix-value dup (expr-check-bit)
389 fix-disp asm-emit:bit-c!
392 : put-bit ( value -- )
393 asm-emit:here asm-emit:bit-c!
396 : put-ent ( value -- ) to asm-emit:ent ;
397 : fix-ent ( -- ) fix-value dup (expr-check-addr) put-ent ;
399 : put-clr ( value -- ) to asm-emit:clr ;
400 : fix-clr ( -- ) fix-value dup (expr-check-addr) put-clr ;
406 : (save-expr) ( fixer-cfa -- )
407 ( fix-addr fix-disp fixer-cfa expr-cfa -- )
408 asm-emit:emit-pc asm-emit:pc rot bc-buffer-start
409 asm-labels:remember-expr
412 : (expr-do,) ( cfa-check cfa-emit cfa-fix -- )
413 bc-buffer-start bc-run if ( cfa-check cfa-emit cfa-fix value )
414 >r rot r@ swap execute
415 drop r> swap execute-tail
416 else ( cfa-check cfa-emit cfa-fix error )
417 dup error-undefined-label? if drop (save-expr) 0 swap execute drop
418 else error-message error
423 ;; expression already parsed
424 : expression-addr-postponed, ( -- )
425 ['] (expr
-check
-addr
) ['] asm-emit:addr ['] fix
-addr
429 : expression
-addr
, ( -- )
430 expression expression
-addr
-postponed
,
433 : expression
-addr
-rel8
, ( -- )
435 bc
-buffer
-start bc
-run
if ( value
)
437 dup
(expr
-check
-rel8
)
440 dup error
-undefined
-label?
if drop
441 ['] fix-rel8 (save-expr)
443 else error-message error
449 : expression-byte, ( -- ) expression
450 ['] (expr
-check
-byte
) ['] asm-emit:byte ['] fix
-byte
454 : expression
-ubyte
, ( -- ) expression
455 ['] (expr-check-byte) ['] asm
-emit
:byte
['] fix-ubyte
459 : expression-word, ( -- ) expression
460 ['] (expr
-check
-word
) ['] asm-emit:word ['] fix
-word
464 : expression
-rword
, ( -- ) expression
465 ['] (expr-check-word) ['] asm
-emit
:rword
['] fix-rword
469 : expression-disp+, ( -- ) expression
470 ['] (expr
-check
-disp
+) ['] asm-emit:byte ['] fix
-disp
+
474 : expression
-disp
-, ( -- ) expression
475 ['] (expr-check-disp-) ['] asm
-emit
:neg
-byte
['] fix-disp-
479 : expression-disp, ( add-flag -- )
480 if expression-disp+, else expression-disp-, endif
483 : expression-rst, ( -- addr ) expression
484 ['] (expr
-check
-rst
) ['] asm-emit:rst ['] fix
-rst
489 : expression
-im
, ( -- idx
) expression
490 ['] (expr-check-im) ['] asm
-emit
:im
['] fix-im
495 0 value bit-expr-addr
497 : expression-bit-postponed, ( opc-base -- )
499 bit-expr-addr to bc-buffer-start
500 ['] (expr
-check
-bit
) ['] put-bit ['] fix
-bit
504 : expression
-bit
( opc
-base
-- ) expression bc
-buffer
-start
to bit
-expr
-addr
;
507 : expression
-ent
( -- ) expression
508 ['] (expr-check-addr) ['] put
-ent
['] fix-ent
512 : expression-clr ( -- ) expression
513 ['] (expr
-check
-addr
) ['] put-clr ['] fix
-clr
518 ' bc-run to asm-labels:(eval-expr-cfa)
519 ' error
-undefined
-label?
to asm
-labels
:(err
-undefined?
)
520 ' error-message to asm-labels:(err-message)
523 ;; expression value must be defined here
524 : expression-const ( -- value )
526 bc-buffer-start bc-run ifnot error-message error endif
535 : expr-test ( -- ) \ expr
536 false to asm-lexer:line-start
539 asm-expr:bc-buffer-start asm-expr:bc-buffer-end debug:(decompile-mem)
540 asm-expr:bc-buffer-start asm-expr:bc-run
545 ." error: " dup . asm-expr:error-message ." (" type ." )" cr
550 expr-test 3+4*2 || 5+label && (9+1)*2