BBim2BB

Descripción del contenido de la página

Programa escrito en Vim para convertir al formato estándar de Beta BASIC un código fuente de formato BBim.

Etiquetas:

Este programa escrito en Vim es el corazón de BBim: a partir del código fuente original en formato BBim crea dos ficheros que podrán ser importados por BBimport:

Código fuente

" bbim2bb.vim

" BBim2BB
" Version A-00-20140726
" Copyright (C) 2011,2012,2014 Marcos Cruz (programandala.net)
" License:
" http://programandala.net/license

" This file is part of BBim:
" http://programandala.net/es.programa.bbim

" This program, written in Vim, converts BBim source code
" into an actual Beta BASIC program
" ready to be imported into a ZX Spectrum emulator
" by BBimport.

" Note:
" This file **must** use the Latin1 (ISO 8859-1) encoding.

" ------------------------------
" History

" 2011-08-30: First draft version, based on vim2mb.vim, by the same
" author.

" 2011-08-31: Some improvements, based on sb2sbsasic,vim, by the same
" author.

" 2011-09-01: Renamed to bbim2bb.vim (formerly bb2bbmgt.vim);
" rearranged, based on mbim2mb.vim.

" 2011-09-10: Labels implemented.

" 2011-09-29: Labels checked and finished; renumbering added;
" BBimTokens().

" 2011-09-30: "silent" command used everywhere; custom messages added.

" 2012-01-25: BBimLabels() fixed: lonely labels had to be joined with
" the next line; optional final ":" removed with labels. These fixes
" were first coded for SBim's SBimLabels().

" 2012-01-27: Fixed the greedy subtitution of block comments.

" 2012-01-28: New feature: import-time commands (marked with a
" semicolon at the start of their lines); their line number is removed.

" 2012-01-28: New feature: #firstline command to define the first line
" that will be used to renumber the final program in Beta BASIC.

" 2012-01-28: Improvement: labels are not case sensitive any more.

" 2012-01-28: Improvement: The .BB and .BB.MGT buffers are actually
" closed with "bw" ("wq" kept them on the list).

" 2012-02-16: Fixed?: "c" parameter removed from the search() in
" BBimLabels(). It caused problems in SBim, the parallel project.

" 2012-03-05: New experimental metacommands (not used yet): #define and
" #substitution.

" 2012-03-05: New metacommands: #vim and #include.

" 2012-03-06: Changed all "normal gg" to "call cursor(1,1)".

" 2012-03-06: BBimInclude() fixed: the filename is get from the current
" line with functions instead of normal-mode register commands, because
" normal-mode cursor movement commands depend on the iskeyword
" variable, what depends on the current syntax: the movement is
" different in the .bbim an the .bbim.bb files. 

" 2012-03-08: Clearer substitutions with nr2char() instead inline bytes.
" Added the conversion of embedded bytes (in BASin format: \#nnn).

" 2012-11-17: Usage message at the start. Fixed: "nul" to "/dev/null"

" 2012-12-06: Fixed: 'normal w"lyw' had to be 'normal ww"lyw'. Detected in
" MBim2BB. Improved: '#vim' and '#include' are counted and the count is
" included in the final messages.

" 2014-06-06: Fix: 'l:includedFiles' was not initialized or updated.

" 2014-07-26:
" Improvevement: Possible trailing or leading spaces are removed from
" every regexp in BBimLabels(), because the source code has been already
" cleaned.
" Fix: The regexp used to substitute label references with their values lacked
" a word ending mark; this caused, e.g., the search for '@theEnd' matched
" '@theEnd01'! Beside, also in BBimLabels(), 'set ignorecase' was needed by
" search().  This bugs were found during the development of SinBasic
" (http://programandala.net/en.program.sinbasic.html).  Fix:

" ------------------------------
" To-do

" Make it possible to write
" several programs in the same BBim source file:
" they would be renumbered apart but exported together
" into one single MGT image, and then imported
" and divided with one single operation.

" ----------------------------------------------

function! BBimClean()

  " Clean off all BBim stuff.

  silent! %s/^\s*#.*$//e " Remove the metacomments
  silent! %s/\s*\/\/.*$//e " Remove the // line comments
  silent %s,^\s*\/\*\_.\{-}\*\/,,e " Remove the /* */ block comments

  silent! %s/^\s*\d\+\s*$//e " Remove lines with the line number only
  " 2012-01-29 xxx old:
  "silent! %s/^\s\+//e " Remove empty lines
  "silent! %s/\n\n\+/\r/eg " Remove empty lines
  " 2012-01-29 xxx new, untested:
  silent! %s/^\n//e " Remove empty lines

  silent %s,^\s*\n,,ge " Remove the empty lines

  silent! %s/^\s*//eg " Remove main indentation
  silent! %s/\s\+$//eg " Remove ending blanks

  silent! %s/\\\s*\n//e " Join the splitted lines
  silent! %s/^\(\d\+\)\s\+/\1/e " Remove the space after the line number

  " Experimental, not implemented yet:
  "silent! %s/^\s*endprog\s\+\([a-zA-Z_-]\+\)/save d*"\1"line 10:delete 10 to/e

  echo 'Source code cleaned.'

endfunction

" ----------------------------------------------
" Metacommands

function! BBimDefine()

  " xxx experimental Not used yet. BBimVim() can do this and more.

  " Execute all #define metacommands.
  " Syntax:
  " #define regexp new_content

  " Empty dictionary to store the substitutions:
  let l:substitution={}

  call cursor(1,1) " Go to the top of the file.
  " Main loop:
  while search('^\s*#define\s\+\S\+\>','Wc')
    " Store the parameters into register 'p':
    normal w"py$
    " Copy to a variable:
    let l:parameters=getreg('p',1)
    let l:new=matchstr(l:parameters,'\S\+$')
    " xxx debug check
    "echo 'parameters='.l:parameters
    let l:old=matchstr(l:parameters,'^\S\+')
    let l:new=matchstr(l:parameters,'\S\+$')
    " xxx debug check
    "echo 'old='.l:old
    let l:new=matchstr(l:parameters,'\S\+$')
    " xxx debug check
    "echo 'new='.l:new
    " Store them into the dictionary:
    let l:substitution[l:old]=l:new
    " Remove the whole line:
    normal dd
  endwhile

  " Do all substitutions:
  for l:old in keys(l:substitution)
    " xxx debug check
    "echo 'Searching...'.l:old
    call cursor(1,1) " Go to the top of the file.
    " Do the subtitution:
    while search(l:old,'Wc')
      " xxx debug check
      'echo l:old 'label reference found'
      execute 'silent! substitute/'.l:old.'/'.l:substitution[l:old].'/ei'
    endwhile
  endfor

endfunction

function! BBimSubstitute()

  " xxx experimental Not used yet. BBimVim() can do this and more.

  " Execute all #substitute metacommands.

  " The #substitute's syntax is identical to Vim's substitute command,
  " but no range or flags can be indicated.

  " No syntax check is done.
  " If the #substitute command is not recognized,
  " it will be part of the Beta BASIC code and will fail at importing-time.
  " If the #substitute command's syntax is not fine,
  " it will fail at translation-time.

  " Examples:
  " #substitute/first_element/second_element/
  " #substitute,first_element,second_element,

  " Empty list to store the substitutions:
  let l:substitution=[]

  call cursor(1,1) " Go to the top of the file.
  " Get all substitutions:
  while search('^\s*#substitute[[:punct:]]','Wc')
    " Store the parameters into register 's':
    normal w"sy$
    " xxx debug check:
    "echo getreg('s',1)
    " Store them into the list:
    call add(l:substitution,getreg('s',1))
    " Remove the whole line:
    normal dd
  endwhile

  " xxx debug check:
  "echo l:substitution

  " Do all substitutions:
  for l:item in l:substitution
    execute 'silent! %substitute'.l:item.'eg'
  endfor

endfunction

function! BBimVim()

  " Execute all #vim metacommands.
  " Syntax:
  " #vim Any-Vim-Ex-Command

  call cursor(1,1) " Go to the top of the file.
  let l:vimCommands=0 " Counter
  while search('^\s*#vim\s','Wc')
    let l:vimCommands += 1
    let l:vimCommandLine = line('.')
    let l:vimCommand=matchstr(getline(l:vimCommandLine),'\S\+.*',4)
    execute 'silent! '.l:vimCommand
    call cursor(l:vimCommandLine,1) " Return to the command line.
    call setline('.','') " Blank the line.
  endwhile

  if l:vimCommands==0
    echo 'No Vim command found.'
  elseif l:vimCommands==1
    echo 'One Vim command executed.'
  else
    echo l:vimCommands 'Vim commands executed.'
  endif

endfunction

function! BBimInclude()

  " Execute all #include commands in the source.
  " Syntax:
  " #include file-name
  " Warning: nested including is possible, but no recursion check is made!

  call cursor(1,1) " Go to the top of the file.
  let l:includedFiles=0 " Counter
  while search('^\s*#include\s','Wc')
    let l:includedFiles += 1
    let l:fileName=matchstr(getline('.'),'\S\+.*',8)
    call setline('.','') " Blank the line.
    " ----------- xxx debug check
    "echo '#include ' l:fileName
    "echo 'getcwd()=' getcwd()
    "echo 'Modifications:'
    "echo ':~' fnamemodify(l:fileName,':~')
    "echo ':p' fnamemodify(l:fileName,':p')
    " -----------
    execute "silent! r ".l:fileName
  endwhile

  if l:includedFiles==0
    echo 'No file included.'
  elseif l:includedFiles==1
    echo 'One file included.'
  else
    echo l:includedFiles 'files included.'
  endif

endfunction

" ----------------------------------------------
" Labels

function! BBimGetFirstLine()

  " Store into s:firstLine the first line number
  " to be used by the final Beta BASIC program
  " (old versions of BBimport occupied lines 1-9).
  " The command #firstline can be used to set
  " the desired line number. Only the first occurence
  " of #firstline will be used; it can be anywhere
  " in the source but always at the start of a line
  " (with optional indentation).

  let s:firstLine=1 " default value

  call cursor(1,1) " Go to the top of the file.
  if search('^\s*#firstline\s\+[0-9]\+\>','Wc')
    " Store the number into register 'l':
    normal ww"lyw
    " And then into the variable:
    let s:firstLine=getreg('l',1)
  endif
  echo 'First line number: '.s:firstLine

endfunction

function! BBimLabels()

  let l:ignoreCaseBackup=&ignorecase
  set noignorecase

  " Join lonely labels to the next line:
  silent %substitute,^\(\(label\s\+\)\?@[0-9a-zA-Z_]\+\)\n,\1:,ei

  " Empty dictionary to store the line numbers of the labels; the labels will be used as keys:
  let l:lineNumber={}
  call cursor(1,1) " Go to the top of the file.
  " Store every label in the l:lineNumber dictionary:
  while search('^\(label\s\+\)\?@[0-9a-zA-Z_]\+\>','W')
    " Store the label into register 'l':
    normal "l2yw
    " xxx debug check
    "echo 'Raw label found: <' . getreg('l',1) . '>'
    " If 'label' is present, go to the next word and repeat:
    if tolower(getreg('l',1))=='label @'
      normal w"l2yw
      " xxx debug check
      "echo 'Actual raw label found: <' . getreg('l',1) . '>'
    endif
    " Remove possible ending spaces:
    let l:label=tolower(substitute(getreg('l',1),' ','','g'))
    " xxx debug check
    "echo 'Clean label: <' . l:label . '>'
    " Use the label as the key to store the line number:
    let l:lineNumber[l:label]=line('.')+s:firstLine-1
    " Go to the next word:
    normal w
  endwhile

  " xxx debug check
  "echo l:lineNumber

  " Remove all labels:
  silent! %substitute/^\(label\s\+\)\?@[0-9a-zA-Z_]\+\s*:\?//ei

  " Substitute every label reference with its line number:
  for l:label in keys(l:lineNumber)
    call cursor(1,1) " Go to the top of the file.
    " Do the subtitution:
    while search(l:label.'\>','Wc')
      " xxx debug check
      "echo l:label "label reference found"
      execute 'silent! substitute/'.l:label.'\>/'.l:lineNumber[l:label].'/ei'
    endwhile
  endfor

  if l:ignoreCaseBackup
    set ignorecase
  else
    set noignorecase
  endif

  echo 'Labels translated.'

endfunction

" ----------------------------------------------
" Renum

function! BBimRenum()

  " Call the the nl program (part of the Debian coreutils package):
  execute "silent! %!nl --body-numbering=t --number-format=rn --number-width=5 --number-separator=' ' --starting-line-number=".s:firstLine." --line-increment=1"

  " In older versions of coreutils,
  " -v sets the first line number, and -i sets the line increment.
  " (the long option for -v doesn't work, though the manual mentions it).
  " Modern versions of nl uses the clearer options
  " --first-line and --line-increment, see:
  " http://www.gnu.org/software/coreutils/manual/coreutils.html#nl-invocation

  " Remove spaces before line numbers
  " (nl has no option to remove them):
  silent! %substitute/^\s*//e

  " Remove line numbers from import-time commands
  silent! %substitute/^[0-9]\{1,4}\s://e

  echo 'Line numbers added.'

endfunction

" ----------------------------------------------
" Character translation

function! BBimTokens()

  " Translate special tokens into its ZX Spectrum code.

  silent! %s/<=/\=nr2char(199)/eg
  silent! %s/>=/\=nr2char(200)/eg
  silent! %s/<>/\=nr2char(201)/eg

endfunction

function! BBimISOchars()

  " Translate ISO 8859-1 chars into ZX Spectrum chars.
  " (Unfinished).

  " The occupied chars must be redefined
  " by the final Beta BASIC program,
  " so this function must be customized ad hoc.

  " silent! %s/¡/\=nr2char(000)/ge
  " silent! %s/¿/\=nr2char(000)/ge
  " silent! %s/Á/\=nr2char(000)/ge
  " silent! %s/É/\=nr2char(000)/ge
  " silent! %s/Í/\=nr2char(000)/ge
  " silent! %s/Ñ/\=nr2char(000)/ge
  " silent! %s/Ó/\=nr2char(000)/ge
  " silent! %s/Ú/\=nr2char(000)/ge
  " silent! %s/Ü/\=nr2char(000)/ge
  " silent! %s/á/\=nr2char(000)/ge
  " silent! %s/é/\=nr2char(000)/ge
  " silent! %s/í/\=nr2char(000)/ge
  " silent! %s/ñ/\=nr2char(000)/ge
  " silent! %s/ó/\=nr2char(000)/ge
  " silent! %s/ú/\=nr2char(000)/ge
  " silent! %s/ü/\=nr2char(000)/ge

endfunction

function! BBimBlockGraphs()

  " Translate BASin format ZX Spectrum block graphics notation
  " (chars 128-143)

  silent! %s/\\  /\=nr2char(128)/ge
  silent! %s/\\ '/\=nr2char(129)/ge
  silent! %s/\\' /\=nr2char(130)/ge
  silent! %s/\\''/\=nr2char(131)/ge
  silent! %s/\\ \./\=nr2char(132)/ge
  silent! %s/\\ :/\=nr2char(133)/ge
  silent! %s/\\'\./\=nr2char(134)/ge
  silent! %s/\\':/\=nr2char(135)/ge
  silent! %s/\\\. /\=nr2char(136)/ge
  silent! %s/\\\.'/\=nr2char(137)/ge
  silent! %s/\\: /\=nr2char(138)/ge
  silent! %s/\\:'/\=nr2char(139)/ge
  silent! %s/\\\.\./\=nr2char(140)/ge
  silent! %s/\\\.:/\=nr2char(141)/ge
  silent! %s/\\:\./\=nr2char(142)/ge
  silent! %s/\\::/\=nr2char(143)/ge

endfunction

function! BBimUDG()

  " Translate BASin format ZX Spectrum UDG notation
  " (chars 144-164)

  silent! %s/\\[Aa]/\=nr2char(144)/ge
  silent! %s/\\[Bb]/\=nr2char(145)/ge
  silent! %s/\\[Cc]/\=nr2char(146)/ge
  silent! %s/\\[Dd]/\=nr2char(147)/ge
  silent! %s/\\[Ee]/\=nr2char(148)/ge
  silent! %s/\\[Ff]/\=nr2char(149)/ge
  silent! %s/\\[Gg]/\=nr2char(150)/ge
  silent! %s/\\[Hh]/\=nr2char(151)/ge
  silent! %s/\\[Ii]/\=nr2char(152)/ge
  silent! %s/\\[Jj]/\=nr2char(153)/ge
  silent! %s/\\[Kk]/\=nr2char(154)/ge
  silent! %s/\\[Ll]/\=nr2char(155)/ge
  silent! %s/\\[Mm]/\=nr2char(156)/ge
  silent! %s/\\[Nn]/\=nr2char(157)/ge
  silent! %s/\\[Oo]/\=nr2char(158)/ge
  silent! %s/\\[Pp]/\=nr2char(159)/ge
  silent! %s/\\[Qq]/\=nr2char(160)/ge
  silent! %s/\\[Rr]/\=nr2char(161)/ge
  silent! %s/\\[Ss]/\=nr2char(162)/ge
  silent! %s/\\[Tt]/\=nr2char(163)/ge
  silent! %s/\\[Uu]/\=nr2char(164)/ge

endfunction

function! BBimChars()

  let l:ignoreCaseBackup=&ignorecase
  set noignorecase

  call BBimISOchars()
  call BBimTokens()
  call BBimUDG()
  call BBimBlockGraphs()

  " Embedded ASCII codes (BASin format):
  silent! %s/\\#\(\d\+\)/\=nr2char(submatch(1))/g

  echo 'Special chars translated.'

  if l:ignoreCaseBackup
    set ignorecase
  else
    set noignorecase
  endif

endfunction

" ----------------------------------------------
" Fake MGT disk image

function! BBimMGTfile()

  " Create a fake MGT disk image from the current BB file

  silent write " Write the current BB file
  split " Split the window
  silent write! %.mgt " Save a copy with the MGT extension added
  silent edit ++bin %.mgt " Open it in binary mode
  set noendofline " Don't put an EOL at the end of the file when saving it
  set fileencoding=latin1

  " Translate line feed chars (decimal 10)
  " into carriage returns chars (decimal 13):
  silent %!tr "\12" "\15"

  " Convert it into a fake MGT disk image,
  " just making it 819200 bytes long:
  silent %!dd bs=819200 conv=sync 2>> /dev/null

  echo 'Fake MGT file created.'

endfunction

" ----------------------------------------------
" BB file

function! BBimBBfile()

  " Create a copy of the current BBim file
  " with the ".bb" extension added
  " and open it for editing.

  silent update " Write the current BBim file if needed
  split " Split the window
  silent write! %.bb " Save a copy with the BB extension added
  silent edit %.bb " Open it for editing
  set fileencoding=latin1

  " xxx note: fileformat does not work here (it makes the renumbering not to work, because all the code is one line):
  "set fileformat=mac " Force CR (char 13) as end of line

  " Force an end of line at the end of the file:
  silent! %substitute/\%$/\r/e
  silent write

  echo 'BB file created.'

endfunction

" ----------------------------------------------
" Main

function! BBim2BB()

  set shortmess=at

  call BBimGetFirstLine()

  let s:ignoreCaseBackup=&ignorecase
  set ignorecase

  call BBimBBfile()

  call BBimInclude()
  call BBimVim()
  call BBimClean()
  call BBimLabels()
  call BBimRenum()
  call BBimChars()

  call BBimMGTfile()
  silent w
  silent bw
  echo 'Fake MGT file saved and closed.'

  set fileformat=mac " Force CR (char 13) as end of line
  silent w
  silent bw
  echo 'BB file saved and closed.'

  if s:ignoreCaseBackup
    set ignorecase
  else
    set noignorecase
  endif

  echo 'Done!'

endfunction

" Shortkey ',bb' in normal mode
" to create a Beta BASIC file:
nmap <silent> ,bb :call BBim2BB()<CR>

echo "BBim2BB loaded."
echo "Activate it with the keys ',bb' (comma, B and B), in normal mode, on your BBim source."

Descargas

Todos los componentes de BBim pueden descargarse en la sección de descargas de BBim.

Páginas relacionadas

MBim2MB
Programa escrito en Vim para convertir al formato estándar de MasterBASIC un código fuente en formato MBim.
SBim
Preprocesador para S*BASIC