about summary refs log blame commit diff stats
path: root/baremetal/shell/line.mu
blob: 32b1422bdcda96402dbde83eed870ed6127b1513 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13












                                                       

                                                           

















































                                                                                                                 






                                                        














                                                                                     



























                                                                                                                                        
                                     


















                                                                                                                                                                         
                               
                                        
                                






                                                            

                                                                                                           

                            
               

                             
                                                                                                         



                                          










                                                                            



                                                                                     
                                                                                     

                                                 
 
                                





                                                          
                                   



                                                                            




                                                                                
                                                                                


                                                 







                                                          
                                   








                                                                                             
                                                                                             

 

                                            
                                                                

                                                             







                                                                     









                                                   
   







                                                                                    




                         
type line {
  name: (handle array byte)
  data: (handle word)
  cursor: (handle word)
  next: (handle line)
  prev: (handle line)
}

# initialize line with a single empty word
fn initialize-line _line: (addr line) {
  var line/esi: (addr line) <- copy _line
  var word-ah/eax: (addr handle word) <- get line, data
  allocate word-ah
  var cursor-ah/ecx: (addr handle word) <- get line, cursor
  copy-object word-ah, cursor-ah
  var word/eax: (addr word) <- lookup *word-ah
  initialize-word word
}

fn num-words-in-line _in: (addr line) -> _/eax: int {
  var in/esi: (addr line) <- copy _in
  var curr-ah/ecx: (addr handle word) <- get in, data
  var result/edi: int <- copy 0
  {
    var curr/eax: (addr word) <- lookup *curr-ah
    compare curr, 0
    break-if-=
    curr-ah <- get curr, next
    result <- increment
    loop
  }
  return result
}

fn line-list-length lines: (addr handle line) -> _/eax: int {
  var curr-ah/esi: (addr handle line) <- copy lines
  var result/edi: int <- copy 0
  {
    var curr/eax: (addr line) <- lookup *curr-ah
    compare curr, 0
    break-if-=
    curr-ah <- get curr, next
    result <- increment
    loop
  }
  return result
}

fn render-line screen: (addr screen), _line: (addr line), x: int, y: int, render-cursor?: boolean -> _/eax: int {
  var line/eax: (addr line) <- copy _line
  var first-word-ah/esi: (addr handle word) <- get line, data
  # cursor-word
  var cursor-word/edi: int <- copy 0
  compare render-cursor?, 0/false
  {
    break-if-=
    var cursor-word-ah/eax: (addr handle word) <- get line, cursor
    var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah
    cursor-word <- copy _cursor-word
  }
  #
  var result/eax: int <- render-words screen, first-word-ah, x, y, cursor-word
  return result
}

fn parse-line in: (addr array byte), _out: (addr line) {
  var out/edi: (addr line) <- copy _out
  initialize-line out
  var dest/eax: (addr handle word) <- get out, data
  parse-words in, dest
}

#? fn main {
#?   # line = [aaa, bbb, ccc, ddd]
#?   var line-storage: line
#?   var w-ah/eax: (addr handle word) <- get line-storage, data
#?   allocate-word-with w-ah, "aaa"
#?   append-word-at-end-with w-ah, "bbb"
#?   append-word-at-end-with w-ah, "ccc"
#?   append-word-at-end-with w-ah, "ddd"
#?   var cursor-ah/ecx: (addr handle word) <- get line-storage, cursor
#?   var w/eax: (addr word) <- lookup *w-ah
#?   var next-ah/eax: (addr handle word) <- get w, next
#?   copy-object next-ah, cursor-ah
#?   var line-addr/eax: (addr line) <- address line-storage
#?   var dummy/eax: int <- render-line 0/screen, line-addr, 0/x, 0/y, 1/render-cursor
#? }

fn render-line-with-stack screen: (addr screen), _line: (addr line), x: int, y: int, render-cursor?: boolean -> _/eax: int, _/ecx: int {
  var line/esi: (addr line) <- copy _line
  # cursor-word
  var cursor-word/edi: int <- copy 0
  compare render-cursor?, 0/false
  {
    break-if-=
    var cursor-word-ah/eax: (addr handle word) <- get line, cursor
    var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah
    cursor-word <- copy _cursor-word
  }
  #
  var curr-word-ah/eax: (addr handle word) <- get line, data
  var _curr-word/eax: (addr word) <- lookup *curr-word-ah
  var curr-word/edx: (addr word) <- copy _curr-word
  var new-x/eax: int <- copy x  # increases each iteration
  var new-y/ebx: int <- copy y  # compute max across all iterations
  {
    compare curr-word, 0
    break-if-=
    var curr-y/ecx: int <- copy 0
    new-x, curr-y <- render-word-with-stack-and-cursor screen, line, curr-word, new-x, y, cursor-word
    compare curr-y, new-y
    {
      break-if-<=
      new-y <- copy curr-y
    }
    new-x <- add 1/inter-word-spacing
    # update
    var next-word-ah/eax: (addr handle word) <- get curr-word, next
    var next-word/eax: (addr word) <- lookup *next-word-ah
    curr-word <- copy next-word
    loop
  }
  return new-x, new-y
}

fn render-word-with-stack-and-cursor screen: (addr screen), line: (addr line), curr-word: (addr word), x: int, y: int, _cursor-word-addr: int -> _/eax: int, _/ecx: int {
  # print curr-word, with cursor if necessary
  var render-cursor?/eax: boolean <- copy 0/false
  var cursor-word-addr/ecx: int <- copy _cursor-word-addr
  {
    compare cursor-word-addr, curr-word
    break-if-!=
    render-cursor? <- copy 1/true
  }
  var new-x/eax: int <- render-word screen, curr-word, x, y, render-cursor?
  add-to x, 1/word-stack-indent
  var new-x-saved/edx: int <- copy new-x
  add-to y, 2/word-stack-spacing
  # compute stack until word
  var stack-storage: value-stack
  var stack/edi: (addr value-stack) <- address stack-storage
  evaluate line, curr-word, stack
  # render stack
  var new-y/ecx: int <- copy 0
  new-x, new-y <- render-value-stack screen, stack, x, y
#?   draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, new-x, 0xc/fg, 0/bg
#?   draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, new-y, 3/fg, 0/bg
  compare new-x, new-x-saved
  {
    break-if->=
    new-x <- copy new-x-saved
  }
#?   draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, new-x, 7/fg, 0/bg
  return new-x, new-y
}

fn test-render-line-with-stack-singleton {
  var line-storage: line
  var line/esi: (addr line) <- address line-storage
  parse-line "1", line
  # setup: screen
  var screen-on-stack: screen
  var screen/edi: (addr screen) <- address screen-on-stack
  initialize-screen screen, 0x20, 4
  #
  var new-x/eax: int <- copy 0
  var new-y/ecx: int <- copy 0
  new-x, new-y <- render-line-with-stack screen, line, 0/x, 0/y, 0/no-cursor
  check-screen-row screen, 0/y, "1   ", "F - test-render-line-with-stack-singleton/0"
  check-screen-row screen, 1/y, "    ", "F - test-render-line-with-stack-singleton/1"
                                # ___
  check-screen-row screen, 2/y, "  1 ", "F - test-render-line-with-stack-singleton/2"
  check-screen-row screen, 3/y, "    ", "F - test-render-line-with-stack-singleton/3"
  # not bothering to test hash colors for numbers
}

fn test-render-line-with-stack {
  var line-storage: line
  var line/esi: (addr line) <- address line-storage
  parse-line "1 2", line
  # setup: screen
  var screen-on-stack: screen
  var screen/edi: (addr screen) <- address screen-on-stack
  initialize-screen screen, 0x20, 8
  #
  var new-x/eax: int <- copy 0
  var new-y/ecx: int <- copy 0
  new-x, new-y <- render-line-with-stack screen, line, 0/x, 0/y, 0/no-cursor
  check-screen-row screen, 0/y, "1    2   ", "F - test-render-line-with-stack/0"
  check-screen-row screen, 1/y, "         ", "F - test-render-line-with-stack/1"
                                # ___  ___
  check-screen-row screen, 2/y, "  1    2 ", "F - test-render-line-with-stack/2"
  check-screen-row screen, 3/y, "       1 ", "F - test-render-line-with-stack/3"
  check-screen-row screen, 4/y, "         ", "F - test-render-line-with-stack/4"
  # not bothering to test hash colors for numbers
}

# { } groups have no effect on the stack by default.
fn test-render-line-with-stack-groups {
  var line-storage: line
  var line/esi: (addr line) <- address line-storage
  parse-line "{ 1 2 }", line
  # setup: screen
  var screen-on-stack: screen
  var screen/edi: (addr screen) <- address screen-on-stack
  initialize-screen screen, 0x20, 8
  #
  var new-x/eax: int <- copy 0
  var new-y/ecx: int <- copy 0
  new-x, new-y <- render-line-with-stack screen, line, 0/x, 0/y, 0/no-cursor
  check-screen-row screen, 0/y, "{  1    2    } ", "F - test-render-line-with-stack-groups/0"
  check-screen-row screen, 1/y, "               ", "F - test-render-line-with-stack-groups/1"
                                #    ___  ___
  check-screen-row screen, 2/y, "     1    2    ", "F - test-render-line-with-stack-groups/2"
  check-screen-row screen, 3/y, "          1    ", "F - test-render-line-with-stack-groups/3"
  check-screen-row screen, 4/y, "               ", "F - test-render-line-with-stack-groups/4"
}

fn edit-line _self: (addr line), key: byte {
  var self/esi: (addr line) <- copy _self
  var cursor-word-ah/edx: (addr handle word) <- get self, cursor
  var _cursor-word/eax: (addr word) <- lookup *cursor-word-ah
  var cursor-word/ecx: (addr word) <- copy _cursor-word
  compare key, 0x20/space
  $edit-line:space: {
    break-if-!=
    append-word cursor-word-ah
    var next-word-ah/eax: (addr handle word) <- get cursor-word, next
    copy-object next-word-ah, cursor-word-ah
    return
  }
  # otherwise insert key within current word
  var g/edx: grapheme <- copy key
  add-grapheme-to-word cursor-word, g
  # silently ignore other hotkeys
}

fn main {
  var line-storage: line
  var line/esi: (addr line) <- address line-storage
  initialize-line line
  {
    clear-screen 0/screen
    var dummy1/eax: int <- copy 0
    var dummy2/ecx: int <- copy 0
    dummy1, dummy2 <- render-line-with-stack 0/screen, line, 2/x, 2/y, 1/show-cursor
    {
      var key/eax: byte <- read-key 0/keyboard
      compare key, 0
      loop-if-=
      edit-line line, key
    }
    loop
  }
}