Code archives/File Utilities/Max to Java

This code has been declared by its author to be Public Domain code.

Download source code

Max to Java by Yasha2011
This simple program uses the magic of regular expressions to do some of the grunt-work in converting BlitzMax files to a Java project: it replaces Basic-style keyword-delineated structures and variable declarations with C-style bracing and declarations, as well as a few other minor changes. This isn't supposed to actually produce runnable code, just to skip the incredible tedium of the work of replacing keywords with braces and so on, leaving you free to focus on the actual refactoring logic. With minor changes, it could be applied equally well to C#, which is very similar.

Usage is simple: place in a folder containing all of the BlitzMax files to convert and run it there; it will load and rewrite every .bmx file in the folder, saving the changed versions as .java files (doesn't currently do subfolders, but this should be easy enough to add).

Because it only uses text-replacement rather than actually parsing the BlitzMax source, there are a few places where it will make errors: most type declarations in the middle of expressions or declaration lists will be left in place most of the time, which is an error in C-like languages; it can't tell the difference between an "inline" If-statement that omits "Then" and the start of an If-block structure, and so on.

Notably, the program will try to replace = with == where appropriate, but might get it wrong if you haven't written very conventional Blitz code. The main place where it will definitely get this wrong is if you use an = expression as the argument to a "command" (no parens). This is not intended to be rock-solid for all edge-cases, just convenient for very basic code, so the output will definitely need to be checked for errors in this area. A good IDE (such as Visual Studio) will catch almost all of the remaining errors, though.

Although all line numbers should be correct, it also doesn't attempt to spare string-literals or commented out code, so you may have to replace these with pasted versions from the original. Should still be readable though.

The resulting code will not be valid Java, unless the input program was extremely trivial; but it will be close enough to Java that it can be corrected quite easily in most cases, as long as the code is fully-OOP (unlike BlitzMax, Java doesn't really support procedural code at all), and a good IDE will be able to recognise many of the class and control structures. There are quite a few other minor syntactic differences between Max and Java that this doesn't address (no default parameters, different casting syntax, C-like for loops, arrays are objects, etc.). The code will probably also need extensive refactoring before it's good Java (no private members or generic types in Max, for a start).

Requires bah.regex.
' Simple tool to do some batch find-and-replace tasks on all BMX files in the current directory

SuperStrict

Import bah.regex


Local dir:Int = ReadDir(CurrentDir())

If Not dir Then RuntimeError "failed to read current directory"

Repeat
	Local fn:String = NextFile(dir)
	If fn = "" Exit
	If fn = "." Or fn = ".." Or ExtractExt(fn) <> "bmx" Or FileType(fn) <> 1 Then Continue
	
	Local bsource:String = LoadText(fn)
	Local jsource:String = ReformatSource(bsource)
	SaveText(jsource, StripExt(fn) + ".java")
Forever

CloseDir dir

End


Function ReformatSource:String(bsource:String)
	
	Global regexen:TList
	If regexen = Null Then initRegexen()
	Local processed:String = bsource
	
	For Local r:RPair = EachIn regexen
		processed = r.regx.replaceall(processed, r.repl)
	Next
	
	Return processed
	
	Function initRegexen()
		regexen = New TList		'The order of some of these is important
		
		AddRPair "(?<=\W)Function\h*([\w\d_]+\h*[:!%#\$])", "public static \1"		'Just drop the Function/Method where a type exists
		AddRPair "(?<=\W)Method\h*([\w\d_]+\h*[:!%#\$])", "public \1"
		AddRPair "(?<=\W)Function\h*([\w\d_]+\h*\()", "public static void \1"		'Otherwise, add void
		AddRPair "(?<=\W)Method\h*([\w\d_]+\h*\()", "public void \1"
		
		AddRPair "(?<=\W)(public\h+[^\n']+\))(?!\h+abstract)", "\1 {"		'Opening brace for methods (all of which are public)
		AddRPair "(?<=\W)public\h+([^\n']+\))\h+abstract", "public abstract \1"		'Move abstract to start
		
		AddRPair "(?<=\W)Then\h+([^\n']+)(?='|\r\n)", "{ \1 }"
		AddRPair "(?<=\W)Else(\W+)(?!If)", "} else {\1"		'Else without If
		AddRPair "(?<=\W)Else\h*(?=If\W)", "} else "	'Convert to normal If
		
		AddRPair "(?<=\W)If\W+([^\n'\{]+)(?=\{)", "if ( \1 ) "
		AddRPair "(?<=\W)If\W+([^\n'\{]+)(?='|\r\n)", "if ( \1 ) {"
		AddRPair "(?<=\W)While\W+([^\n']+)(?='|\r\n)", "while ( \1 ) {"
		AddRPair "(?<=\W)For\W+([^\n']+)(?='|\r\n)", "for ( \1 ) {"
		AddRPair "(?<=\W)Select\W+([^\n']+)(?='|\r\n)", "switch ( \1 ) {"
		AddRPair "(?<=\W)Until\W+([^\n']+)(?='|\r\n)", "} while (!( \1 ));"
		
		AddRPair "(end(\h*)(method|function|type|while|try|if|select))", "} // \1"
		AddRPair "(?<=\W)(next|wend)(?=\W)", "} // \1"
				
		AddRPair "Extends", "extends"	'For reasons of case sensitivity
		AddRPair "Abstract", "abstract"
		AddRPair "Null", "null"
		AddRPair "(?<=\W)Self(?=\W)", "this"	'Java doesn't call it Self
		AddRPair "Super", "super"
		AddRPair "Int", "int"
		AddRPair "Float", "float"
		AddRPair "String", "String"
		AddRPair "Return", "return"
		AddRPair "Case", "case"
		AddRPair "Default", "default"
		AddRPair "Throw", "throw"
		AddRPair "True", "true"
		AddRPair "False", "false"
		AddRPair "(?<=\W)Exit(?=\W)", "break"
		AddRPair "Continue", "continue"
		AddRPair "New\h+([\w\d_]+)", "new \1()"
		AddRPair "(DebugLog)", "// \1"		'Add these again later, ignore for now
		
		AddRPair "(?<=\W)(Local)(?=\h)", "/* \1 */"		'Keep the declaration, to help check converted code
		AddRPair "(?<=\W)Field\h*", "public "			'All BlitzMax fields are public
		AddRPair "(?<=\W)Global\h*", "public static "	'This will be wrong for globals in functions
		AddRPair "(?<=\W)(Const)\h*", "/* \1 */ public static final "
		
		AddRPair "([\w\d_]+)%", "int \1"		'Convert type sigils to keywords
		AddRPair "([\w\d_]+)#", "float \1"
		AddRPair "([\w\d_]+)!", "double \1"
		AddRPair "([\w\d_]+)\$", "String \1"
		AddRPair "([\w\d_]+)(\h*):(\h*)([\w\d_\[\]]+)", "\4 \1"		'Swap name:type declaration syntax around
		
		AddRPair "([\.=\+\-\*&\|<>]\h*)(int|float|double|String)\h", "\1"	'Remove converted inline-sigils where obvious
		AddRPair "(?<!\*)(/\h*)(int|float|double|String)\h", "\1"			'Slash only when not a closing comment
		
		AddRPair "(?<=\W)Repeat(?=\W)", "do {"
		AddRPair "(?<=\W)Forever(?=\W)", "} while (true);"
		AddRPair "<>", "!="
		AddRPair "=>", ">="
		AddRPair "=<", "<="
		AddRPair "(?<=\W)And(?=\W)", "&&"
		AddRPair "(?<=\W)Or(?=\W)", "||"
		AddRPair "(?<=\W)Not(?=\W)", "!"
		AddRPair "(?<=\W)Shl(?=\W)", "<<"
		AddRPair "(?<=\W)Shr(?=\W)", ">>"
		AddRPair "(?<=\W)Sar(?=\W)", ">>>"
		AddRPair "(?<=\W)Mod(?=\W)", "%"
		AddRPair ":(\+|-|\*|/|&|\||~~|<<|>>|>>>|%)", "\1="		'Change compound assignment style
		
		AddRPair "(?<=\W)(class)(?=\W)", "\1_"		'Add any Java keywords in use as identifiers here
		
		AddRPair "(abstract\h+)?type\h+([\w\d_]+(\hextends\h([\w\d_]+))?)", "public \1class \2 {"	'Type to class
		AddRPair "([^\s\{\};\.,])(\h*)(?='|\r\n|\})", "\1;\2"		'Add semicolons where statements end
		
		AddRPair "(\[[^,\n\]]+),", "\1]["		'Replace commas in array elements with ][ - may not want this
		
		AddRPair "'", "//"			'Comments
		AddRPair "(?<=\W)end(\h*)rem(?=\W)", "*/"
		AddRPair "(?<=\W)Rem(?=\W)", "/*"
		
		AddRPair "=\h*EachIn(?=\W)", " : "		'Once the type signatures are done this is safe
		
		AddRPair "(\[[^\n\]=]+)=", "\1=="			'Replace = with == within array[element] access
		'Try to replace = with == where appropriate - this may be horribly wrong!
		AddRPair "(?<=[\n;,\{])(((?!\(|\Wreturn\h|=)[^\n=;,\{])+(\(|\Wreturn\h|=)[^\n=;,]+)(?<=[^\!<>])=(?!=)", "\1=="
	End Function
	
	Function AddRPair(regx:String, repl:String)
		regexen.AddLast(RPair.Create(regx, repl))
	End Function
End Function

Type RPair
	Field regx:TRegEx, repl:String
	
	Function Create:RPair(regx:String, repl:String)
		Local r:RPair = New RPair
		r.regx = TRegEx.Create(regx) ; r.repl = repl
		Return r
	End Function
End Type

Comments

Jesse2011
nice!
now implement that for Javascript so I can use it. :)


Code Archives Forum