« first day (1001 days earlier)      last day (1709 days later) » 

5:03 PM
posted on March 29, 2018 by CommitStrip

 
5:34 PM
I'm here
advanceBy' :: Int -> BFProgram -> BFProgram
advanceBy' advancement (BFProgram past current next) = do
    let next' = current:next
    let next'' = drop advancement next'
    BFProgram (past ++ take advancement next') (head next'') (tail next'')

decreaseBy' :: Int -> BFProgram -> BFProgram
decreaseBy' decrement (BFProgram past current next) = do
    let past' = past ++ [current]
    let length' = length past' - decrement
    let past'' = take length' past'
    BFProgram (init past'') (last past'') (drop length' past' ++ next)
Do these look solid?
 
Err.
It would be a lot easier to reason if you've used splitAt.
 
I'm pretty sure I have an off-by-one when using these :P
My findMatchingLoopClose calculates how many instructions the ] is beyond the character after the current [
 
let (next', (newCurrent:newNext)) = splitAt advancement (current:next)
in BFProgram (past ++ next') newCurrent newNext
 
@Zeta Woah, that looks way too clear
 
Err. What is BFProgram?
 
5:39 PM
data BFProgram = BFProgram [BFInstruction] BFInstruction [BFInstruction]
 
That shouldn't work in do o.O
 
Huh?
Is my code unclear if I need to calculate some things and cannot do it reasonably, or is that a feature of the loops feature in Haskell ^^
I mean if I have difficulty reasoning about the loop construct
 
@skiwi Oh. Wow. Nevermind. do is really just syntactic sugar.
Since you only have let ...., which gets desugared to let ... in ..., you never use >> nor >>=. I've never realized that.
@skiwi Well, I can show you my BFInstruction for comparison if you want to see how I handled loops.
As I said, it's very similar, but it only contains 7 constructors, not 8.
 
@Zeta I'm wondering if maybe I should
@Zeta Don't show too much :P I want to write an AST-based approach as well
Parsing the loops is going to be rather interesting though
 
Yeah, that's the longest continuous section in my code.
 
5:45 PM
data BFInstruction = MemoryRight | MemoryLeft | Increment | Decrement | Output | Input
data BFAST = BFSimple BFInstruction | BFLoop [BFInstruction]
Might look like this, don't know yet
 
Sounds reasonable, although a loop can contain a loop, e.g. [[x]].
 
Right
Should be BFAST as list right
 
Right.
If you merge BFast and BFInstruction then you'll end up with my variant.
 
Hm that would also work I think yes
I forgot for a moment that you could use the datatype itself recursively as well ^^
Gah, something in my loops is still off-by-1
 
Can't tell without code :P
 
5:49 PM
And now I did the work to reason about it :P
It's also getting a bit.. long
And reverse should be killing the performance, I know :(
 
findMatchingLoopClose nextProgram, but advanceBy also looks at instruction.
 
Huh debuggers cannot show what's in there if I don't implement show ^^
 
data BFInstruction = .... | .... | ...  deriving (Eq, Show)
 
^^
Debugging in VSCode seems rather tricky though
Oh no, I made the mistakes that made it difficult
Looks like I'm missing at least 3 instructions when having one loop iteration done :P
 
To be honest, I would use QuickCheck to check advanceBy' and decreaseBy'. ^^"
 
6:01 PM
QuickCheck?
I also kinda think I should implement a gotoMatchingLoopClose and gotoMatchingLoopOpen functions on the program directly
Hmm wait, I can just advance the memory in place as well
Might be somewhat less efficient, but at least correct!
 
So, advanceBy x (advanceBy (negate x) program) should be program, as long as x is lesser than the length of next, right?
 
@Zeta Correct
But I'm going to scrap those methods anyway
 
import Test.QuickCheck
genInstruction :: Gen BFInstruction
genInstruction = elements [MemoryRight .. Input]

genProgram :: Gen BFProgram
genProgram = BFProgram [] <$> genInstruction <*> listOf genInstruction

advanceBy_prop :: Property
advanceBy_prop =
    forAll genProgram $ \program ->
    forAll (choose (0, maximumAdvance program)) $ \x ->
        advanceBy (negate x) (advanceBy x program) === program
    where
        maximumAdvance (BFProgram _ _ r) = length r
advanceBy_prop checks that property (with quickCheck advanceBy_prop).
 
I'll think about that later ^^
 
Yeah, no worries. Property based testing is fun but would be an overkill for BF :D. Also, there aren't that many interesting properties you can test, I guess.
 
6:21 PM
Another try, another chance
:(
Do I need Eq on my instructions?
 
Still off by one?
 
No, it's worse now :P
 
O.O
@skiwi For (==)? Yes.
 
@Zeta It's also implicitly used by case? Or not
> C:\Users\Frank\Documents\HaskellProjects\brainfuck-interpreter>stack exec brainfuck-interpreter-exe ",[->++<.]>."
!
brainfuck-interpreter-exe.EXE: Prelude.last: empty list
Ohh.. didn't expect this here
Oh wow, made a mistake
And found another bug
@Zeta pastebin.com/PVD9K9Pv ^^ I'm pretty sure +[+] would result in an error right now though, but I'm not even getting that
 
6:39 PM
Did you print the resulting memory cell? It's 2, which indicates that your loop didn't loop.
 
@Zeta Hmm I should do that ^^
@Zeta Which program was that?
 
+[+]
 
Hmm.. that's not correct indeed ^^
That's why it's also that fast!
> C:\Users\Frank\Documents\HaskellProjects\brainfuck-interpreter>stack exec brainfuck-interpreter-exe "+"
Memory: BFMemory [] (BFMemoryCell 0) []
...
WTF
 
Did you change the program inbetween? ^^
 
Why?
Looks like it has an off-by-one on the instructions themselves..
 
6:44 PM
Prelude> :l Skiwi
[1 of 1] Compiling Skiwi            ( D:\Workspace\Haskell\brainfuck\src\Skiwi.hs, interpreted )
Ok, one module loaded.
*Skiwi> interpret "+[+]"
Right (BFMemory [] (BFMemoryCell 2) [])
 
Oh shit
@Zeta Not sure
step (BFProgram _ _ []) memory = return . Right $ memory
This throws away the last instruction
 
Ayup.
 
Hmm, that's not a straight-forward fix
Or well, not that bad
 
"Simple" (as hack) fix: add Stop as instruction, and use step (BFProgram l v []) memory = step (BFProgram l v [Stop]) memory or similar.
 
step program@(BFProgram _ _ []) memory = step program memory
Should work
 
6:50 PM
That's an infinite loop.
 
Oh wait.. it shouldn't
Ah.. duh
step program@(BFProgram [] _ _) memory = step program memory
Except, maybe not quite
 
That's another infinite loop at program start.
 
Why?
It should fall into the second case then
But you're right
 
Your line says step program memory = step program memory (if program has the pattern BFProgram [] _ _)
 
Yeah, I confused things..
Hmm, there's no easy fix without hack, is there?
I'd need to introduce BFContext or something (or use a tuple) and define a step' on that, and then call that one without going into the recursion at the end
 
6:59 PM
step :: BFProgram -> BFMemory -> IO (Either String BFMemory)
step program@(BFProgram _ instruction next) memory@(BFMemory _ currentMemory _) = case instruction of
    MemoryRight -> step' (advance program) (moveMemoryRight memory)
    MemoryLeft  -> step' (advance program) (moveMemoryLeft memory)
    Increment   -> step' (advance program) (onCurrentCell incrementCell memory)
    Decrement   -> step' (advance program) (onCurrentCell decrementCell memory)
    Output      -> do
        putChar . chr . getCell $ currentMemory
That might work. Not tested, though
 
Woot, structure works again,d id it somewhat diffirently though
step :: BFContext -> IO BFMemory
step context@(BFContext (BFProgram _ _ []) _) = do
    (BFContext _ memory') <- calc context
    return memory'
step context = do
    context' <- calc context
    step context'

calc :: BFContext -> IO BFContext
calc (BFContext program@(BFProgram _ instruction _) memory@(BFMemory _ currentMemory _)) = case instruction of
Actually calc should be named step' because it does step
 
Does [-] work as a program?
 
My loop is still broken though
> C:\Users\Frank\Documents\HaskellProjects\brainfuck-interpreter>stack exec -- brainfuck-interpreter-exe "+[+]"
Memory: BFMemory [] (BFMemoryCell 2) []

C:\Users\Frank\Documents\HaskellProjects\brainfuck-interpreter>stack exec -- brainfuck-interpreter-exe "-[-]"
Memory: BFMemory [] (BFMemoryCell 254) []
At least the jumping part seems to work lol
Wait..
 
why are you not just traversing your instruction tree?
I mean ... you got an AST, dealing with loops already...
 
@Vogel612 How do you mean?
There's no AST here yet though
Latest broken code: pastebin.com/DiwxT3Sc
 
7:11 PM
handling an AST would make the function somewhat simpler, because you can recurse and retry
 
@Vogel612 I know, but that's for my next attempt
Gotta be afk for a bit though, while I ponder about why it does not jump back
 
1 hour ago, by skiwi
data BFInstruction = MemoryRight | MemoryLeft | Increment | Decrement | Output | Input
data BFAST = BFSimple BFInstruction | BFLoop [BFInstruction]
He already knows that but wants to follow the LoopBegin | LoopEnd approach till the end.
Who am I to tell someone to quit their dreams nigthmares?
4
 
step :: BFAST -> BFMemory -> IO()
step r@(BFLoop subtree) mem = do
    doSteps program BFMemory
    if current mem /= 0 then step r mem
 
I just can't take that my own program is not executing to as I want ^^
 
that will most likely not compile and not work either... mem is used as a mutable here ...
 
7:36 PM
So far I haven't found a reason that my code shouldn't work, on mobile though now, I'll have to step through when back at PC
 
8:21 PM
step :: BFContext -> IO BFMemory
step context@(BFContext (BFProgram _ _ []) _) = do
    (BFContext _ memory') <- step' context
    return memory'
step context = do
    context' <- step' context
    step context'
Argh this is still wrong
 
 
3 hours later…
11:45 PM
Vogel612 vs. syb0rg: 3101 diff. Year: +342. Quarter: +342. Month: +188. Week: 0. Day: -5.
Kaz vs. Mast: 38 diff. Year: -46. Quarter: -46. Month: -50. Week: -16. Day: -10.
 

« first day (1001 days earlier)      last day (1709 days later) »