advent/2024/03.fs

88 lines
3.7 KiB
Forth

: load-data ( addr1 u1 -- addr2 u2 )
20000 allocate drop \ allocate a 20k buffer, drop the status code
dup 2swap \ duplicate the buffer addr and stick both copies under the addr/length of filename
r/o open-file drop \ open file, drop the status code
20000 swap \ stick the number of bytes to be read in between the buffer addr and file id
read-file drop \ read the file, drop the status code
;
: chop-prefix ( addr u u2 -- addr2 u2 )
\ chop the first `u2` bytes off the beginning of the string at `addr u`
tuck \ duplicate `u2` and store it "under" the length of the string
- \ subtract `u2` from the length of the string
-rot \ stick the new string length underneath the start pointer
+ \ increment the start pointer by `u2`
swap \ put them back in the right order
;
require regexp.fs
: mul-instr ( addr u -- flag )
\ match a string of the form `mul(x,y)` where x and y are integers and capture those integers
(( =" mul(" \( {++ \d ++} \) ` , \( {++ \d ++} \) ` ) )) ;
: get-product ( addr u -- u2 )
mul-instr \ match the string from `addr u` against the above regex
if \ if the regex matches, then:
\1 s>number drop \ convert the first capture from string to number, drop the status code (we already know it will succeed)
\2 s>number drop \ convert the second capture from string to number, drop the status code
* \ multiply, and leave the answer on the stack
else
0 \ otherwise, leave 0 on the stack
then
;
variable result \ initialize `result` with 0
0 result !
variable enabled
-1 enabled ! \ idiomatically -1 is "true" (really anything other than 0 is true)
: handle-mul ( addr u -- )
get-product \ pass those to get-product above
result @ + \ load `result` and add to product
result ! \ store this new value back in `result`
;
: sum-mul-instrs ( addr u -- u2 )
\ we want to loop from addr to (addr + u - 8), because 8 is the min length of a valid mul(x,y) instruction
\ we also want to have addr + u on the top of the stack when we enter the loop,
\ so that we can use that to compute the remaining length of the string from our current address
over + \ copy addr to top of stack and add to length
dup 8 - \ duplicate, then subtract 8 from the top value
rot \ move original addr to top of stack
( stack at this point: [ addr + u, addr + u - 8, addr ] )
( i.e. [ end-of-string, loop-limit, loop-start ] )
do \ start looping
I 4 s" do()" str= \ compare the length-4 substring starting at I to the string "do()"
if \ if valid do() instruction,
-1 enabled ! \ set enabled=true
then
I 7 s" don't()" str= \ compare length-7 substring to "don't()"
if \ if valid don't() instruction,
0 enabled ! \ set enabled=false
then
I 4 s" mul(" str= \ compare length-4 substring to "mul("
enabled @ and \ combine with current value of `enabled`
if \ if a candidate for `mul(x,y)` instruction, and enabled=true, then
dup I - \ subtract current string pointer from end-of-string pointer to get length of remaining string
I swap handle-mul \ put current pointer onto stack again, swap so stack is ( addr len), and handle
then
loop
drop \ get rid of end-of-string pointer
result @ \ return value of result
;
s" data/03.txt" load-data
sum-mul-instrs .
bye