BBim2BB

Descripción del contenido de esta página

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

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:

Listado


" bbim2bb.vim

" BBim2BB
" Copyright (C) 2011,2012 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).

" ------------------------------
" 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 Old!!!:
	"silent! %s/^\s\+//e " Remove empty lines
	"silent! %s/\n\n\+/\r/eg " Remove empty lines
	" 2012-01-29 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()

	" 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\+$')
		" debug!!!
		"echo 'parameters='.l:parameters
		let l:old=matchstr(l:parameters,'^\S\+')
		let l:new=matchstr(l:parameters,'\S\+$')
		" debug!!!
		"echo 'old='.l:old
		let l:new=matchstr(l:parameters,'\S\+$')
		" debug!!!
		"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)
		" debug!!!
		"echo 'Searching...'.l:old
		call cursor(1,1) " Go to the top of the file.
		" Do the subtitution:
		while search(l:old,'Wc')
			" Debug!!!
			'echo l:old 'label reference found'
			execute 'silent! substitute/'.l:old.'/'.l:substitution[l:old].'/ei'
		endwhile
	endfor

endfunction

function! BBimSubstitute()

	" 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$
		" Debug!!!:
		"echo getreg('s',1)
		" Store them into the list:
		call add(l:substitution,getreg('s',1))
		" Remove the whole line:
		normal dd
	endwhile

	" Debug!!!:
	"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.
	while search('^\s*#vim\s','Wc')
		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

	echo 'Vim commands executed.'

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.
	while search('^\s*#include\s','Wc')
		let l:fileName=matchstr(getline('.'),'\S\+.*',8)
		call setline('.','') " Blank the line.
		" ----------- debug!!!
		"echo '#include ' l:fileName
		"echo 'getcwd()=' getcwd()
		"echo 'Modificaciones:'
		"echo ':~' fnamemodify(l:fileName,':~')
		"echo ':p' fnamemodify(l:fileName,':p')
		" -----------
		execute "silent! r ".l:fileName
	endwhile

	echo 'Files included.'

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 w"lyw
		" And then into the variable:
		let s:firstLine=getreg('l',1)
	endif
	echo 'First line number: '.s:firstLine

endfunction

function! BBimLabels()

	" Join lonely labels to the next line:
	silent %substitute,^\s*\(\(label\s\+\)\?@[0-9a-zA-Z_]\+\)\s*\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('^\s*\(label\s\+\)\?@[0-9a-zA-Z_]\+\>','W')
		" Store the label into register 'l':
		normal "l2yw
		" Debug!!!
		"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
			" Debug!!!
			"echo 'Actual raw label found: <' . getreg('l',1) . '>'
		endif
		" Remove possible ending spaces:
		let l:label=tolower(substitute(getreg('l',1),' ','','g'))
		" Debug!!!
		"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
	
	" Debug!!!
	"echo l:lineNumber

	" Remove all labels:
	silent! %substitute/^\s*\(label\s\+\)\?@[0-9a-zA-Z_]\+\s*:\?\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')
			" Debug!!!
			"echo l:label "label reference found"
			execute "silent! substitute/".l:label."\\>/".l:lineNumber[l:label]."/ei"
		endwhile
	endfor

	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>> nul

	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

	" 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>


Descarga

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

Páginas relacionadas

Glosario

BASIC
Beginner's All-Purpose Symbolic Instruction Code (código polivalente de instrucciones simbólicas para principiantes)
BBim
Beta BASIC improved (Beta BASIC mejorado)
MBim
MasterBASIC improved (MasterBASIC mejorado)
S*BASIC
SuperBASIC y/o SBASIC
SBim
S*BASIC improved (S*BASIC mejorado)
Vim
Vi IMproved (Vi mejorado)