MBim2MB

Descripción del contenido de la página

Programa escrito en Vim para convertir al formato estándar de MasterBASIC un código fuente en formato MBim.

Etiquetas:

Este programa escrito en Vim es el corazón de MBim: a partir del código fuente original en formato MBim crea dos nuevos ficheros que podrán ser importados en el emulador SimCoupe de SAM Coupé:

Debido a que el proceso final de importación no puede funcionar, este programa quedó con detalles sin «rematar». La versión análoga para Beta BASIC, BBim2BB, es un ejemplo de lo que MBim2MB debería haber sido.

Código fuente

" mbim2mb.vim

" MBim2MB (version A-02-201406260020)

" This file is part of the MBim tools.

" Copyright (C) 2011,2012,2013,2014 Marcos Cruz (programandala.net)

" This Vim program provides some functions needed to
" convert MBim source code into an actual MasterBASIC
" program ready to be imported into the 
" SimCoupe emulator using the MBimport utility.

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

" 2011-08-07: First version, called "vim2mb.vim".

" 2011-08-10: Added: "set noignorecase" and lowercase UDG chars.

" 2011-08-12: Fixed: "set fileencoding=latin1" was needed to make sure the
" source file is not UTF-8.

" 2011-09-01: Many changes: Renamed to "mbim2mb.vim".  Improved: Two files are
" created from the original "myprogram.mbim": "myprogram.mbim.mb" and
" "myprogram.mbim.mb.mgt". Formerly, the MGT was created right from the
" original code, ready to be imported by "MBimport 2". Now the intermediate MB
" file can be added into a real MGT disk image with the "SAM Diskimage
" manager" by Edwin Blink, and then imported by "MBimport 3".

" 2012-11-16: "silent!" added to the substitutions.

" 2012-11-17: Messages during the process. Usage message at the start. UDG and
" block chars are translated with nr2char() insted of hardcoded char codes.
" Support for embedded ASCII codes (in BASin format). "silent" added to the
" file operations. Fixed: "nul" to "/dev/null"

" 2012-11-18: The fake MGT image is compressed with zip or gzip.
"
" 2012-11-19: The lines can be numbered. This way the new versions of MBimport
" can work faster. The MB file can be deleted; is not needed in the last
" versions of MBimport. Line comments now need a trailing space (this avoids
" the '//' in 'http://' to be considered the start of a comment, and in other
" cases as well).
"
" 2012-11-20: Implemented: '#firstline'.
"
" 2012-12-05: Improved: now '#firstline 0' means no line number (MBimport 5
" imports source with line numbers, what is faster; the previous versions add
" the line numbers during the import proccess). The minimum line number is
" 100, to protect MBimport 5.

" 2012-12-06: Improved: in MBimChars(), the status of 'ignorecase' is
" preserved.  Fixed: 'normal w"lyw' had to be 'normal ww"lyw'. New: '#vim' and
" '#include' (copied from BBim2BB). New: '#keep_mb_file' and '#compression'.

" 2012-12-08: The minimum line number now is 10, because MBimport 5 has only
" seven lines.

" 2012-12-25: '#firstline' changed to '#first_line_number'.

" 2013-01-20: Block comments regexp improved.

" 2014-06-19: Revision. New to-do.

" 2014-06-26: Version A-02. No ISO chars translated anymore. The
" 'delete_MB_file' variable is renamed to 'remove_MB_file', and its default
" value is changed: now the file is keep unless '#remove_mb_file' is used;
" '#keep_mb_file' doesn't work any more. The default first line is 1, not 0
" (what caused the lines were not numbered by default).
" Fix: tow 'silent wq' are changed to 'silent w' and 'silent bw'; previously
" the buffers of the .mb and .mb.mgt files were not actually closed, and
" the next run failed.

" ------------------------------
" XXX TODO

" 2014-07-03: Fix:
" The following deffn with splitted lines removes some lines above it:
"   deffn f$(e)\
"   // comment
"   ="x"
" But this way it is properly rendered:
"   deffn f$(e)=\
"   // comment
"   "x"

" Fix?: This line removes al the following splitted lines:
"    // print i:\ // comment

" Change: #fake_mgt to force the creation of the mgt file. The default
" behaviour will be not to create it, because it will be unnecessary with the
" next version of SimCoupe.

" Improve: Option to translate labels to line numbers? This may prevent
" problems with KEYIN during the import process, and also would make the code
" faster.

" Fix: remove empty lines at the top of the source (after removing the
" comments); test this with the program 'mkusage5'.

" New: #remove_labels, #udg_off, #blocks_1_off, #blocks_2_off

" ----------------------------------------------
" Source cleaning

function! MBimClean()

  " Clean off all MBim stuff.

  " Remove comments:
  silent! %s@^\s*#.*$@@ " metacomments
  silent! %s@^\s*\/\/\s\+.*$@@ " line comments at the start 
  silent! %s@\s\+\/\/\s\+.*$@@ " line comments at the end 
  silent! %s@^\s*\/\*\_.\{-}\*\/@@ " block comments

  " Remove the empty lines:
  silent! %s/^\s\+//
  silent! %s/\n\n\+/\r/g

  silent! %s.^\s*..g " Remove indentation
  silent! %s/\s\+$//g " Remove ending blanks

  silent! %s.\\\n.. " Join the splitted lines 

  echo 'Source code cleaned.'

endfunction

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

function! MBimGetConfig()

  " Search the source for '#keep_mb_file', '#compression zip' and
  " '#compression gzip'.  They can be anywhere in the source but always at the
  " start of a line (with optional indentation).  The values of
  " 's:remove_MB_file' and 's:zip_compression' are set accordingly.

  " Delete the MB file?
  " The MB file is needed only by old versions of MBimport.
  let s:remove_MB_file=0 " Default
  call cursor(1,1) " Go to the top of the file.
  let s:remove_MB_file=search('^\s*#remove_mb_file\>','Wc')
  if s:remove_MB_file
    echo 'The MB file will be removed.'
  else
    echo 'The MB file will be kept.'
  endif

  " Compress the fake MGT file with zip instead of gzip?
  let s:zip_compression=0 " Default
  call cursor(1,1) " Go to the top of the file.
  if search('^\s*#compression\s\+g\?zip\>','Wc')
    " Store the compression format into register 'l':
    normal ww"lyw
    " And then into the config variable:
    let s:zip_compression=(getreg('l',1)=='zip')
  endif
  if s:zip_compression
    echo 'The MGT file will be zipped.'
  else
    echo 'The MGT file will be gzipped.'
  endif

endfunction

function! MBimVim()

  " 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)
    " echo l:vimCommand
    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! MBimInclude()

  " 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

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

function! MBimGetFirstLine()

  " Store into s:firstLine the first line number
  " to be used by the final MasterBASIC program.
  " The command #first_line_number can be used to set
  " the desired line number. Only the first occurence
  " of #first_line_number will be used; it can be anywhere
  " in the source but always at the start of a line
  " (with optional indentation).

  " Default value of the first line number
  " (if it's 0, the lines will not be renumbered):
  let s:firstLine=1 " Default

  call cursor(1,1) " Go to the top of the file.
  if search('^\s*#first_line_number\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
  if s:firstLine
    " Make sure the first line does not clash with MBimport 5:
    let s:firstLine=max([s:firstLine,10])
    echo 'First line number: '.s:firstLine
  else
    echo 'No line numbers'
  endif

endfunction

function! MBimRenum()

  " Call the the nl program (part of the Debian coreutils package):
  execute "silent! %!nl --body-numbering=t --number-format=rn --number-width=6 --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,5}\s://e

  echo 'Line numbers added.'

endfunction

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

" Translations are provided for block graphs and ZX Spectrum UDGs, using the
" BASin notation.
"
" Other ad hoc translations must be done in the program source, using the #vim
" command. Example:
"
"   // Convert every A acute char in the source to SAM Coupé's char 133:
"   #vim %s/Á/\=nr2char(133)/g

function! MBimBlockGraphs()

  " Translate BASin format ZX Spectrum block graphics notation
  " (SAM Coupé 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! MBimUDG()

  " Translate BASin format ZX Spectrum UDG notation
  " (SAM Coupé 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! MBimChars()

  set fileencoding=latin1
  set fileformat=mac " Force CR (char 13) as end of line
  let l:ignoreCaseBackup=&ignorecase
  set noignorecase

  call MBimUDG()
  call MBimBlockGraphs()

  " 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! MBimMGTfile()

  " Create a fake MGT disk image from the
  " current MB file.

  silent write " Write the current MB file
  split " Split the window
  silent write! %.mgt " Save a copy with the MGT extension added
  echo 'Fake MGT file created.'

  silent edit ++bin %.mgt " Open it in binary mode
  set noendofline " Don't put an EOL at the end of the file whew saving it
  set fileencoding=latin1

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

  " Somehow, 'dd' changes all
  " carriage returns (decimal 13) to
  " line feeds (decimal 10), what makes useless
  " the command 'set fileformat=mac' used to
  " force the carriage return needed by the SAM Coupé
  " as end of line. That's why "tr" is needed now:

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

  let mgtfile=expand("%")
  silent w " Write the MGT file
  silent bw " Close the MGT file
  echo 'Fake MGT file saved and closed.'

  " Make a compressed copy of the fake disk image
  " (no check is made; the compression program must be installed):
  if s:zip_compression
    " silent execute '!zip -9' shellescape(mgtfile.'.zip') shellescape(mgtfile)
    silent execute '!zip -9' shellescape(mgtfile.'.zip') shellescape(mgtfile)
    silent call delete(mgtfile)
    echo 'Fake MGT file compressed with zip.' mgtfile
  else
    silent call delete(mgtfile.'.gz')
    silent execute '!gzip' shellescape(mgtfile)
    echo 'Fake MGT file compressed with gzip.'
  endif

endfunction

" ----------------------------------------------
" MB file

function! MBimMBfile()

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

  silent write " Write the current MBim file
  split " Split the window
  silent write! %.mb " Save a copy with the MB extension added
  silent edit %.mb " Open it for editing
  set fileencoding=latin1

  echo 'MB file created.'

endfunction

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

function! MBim2MB()

  call MBimGetFirstLine()
  call MBimGetConfig()
  call MBimMBfile()
  call MBimInclude()
  call MBimVim()
  call MBimClean()
  if s:firstLine
    call MBimRenum()
  endif
  call MBimChars()
  call MBimMGTfile()
  let mbfile=expand("%")
  silent w " Save the MB file
  silent bw " Close the MB file
  echo 'MB file saved and closed.'
  if s:remove_MB_file
    silent echo delete(mbfile)
    echo 'MB file removed.'
  endif

  echo 'Done!'

endfunction

" Shortkey ',mb' in normal mode:
nmap <silent> ,mb :call MBim2MB()<CR>

echo "MBim2MB loaded."
echo "Activate it with the keys ',mb' (comma, M and B), in normal mode, on your MBim source."


Descargas

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

Páginas relacionadas

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