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:
- Con extensión .bb: Fichero de texto con código fuente en Beta BASIC listo para ser añadido a una imagen de disquete MGT e importado con BBimport 2.
- Con extensión .bb.mgt: Falsa imagen de disquete MGT, para ser leída por BBimport 4.
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.