Modul:Vorlage:CompilerExplorerLink

aus Wikipedia, der freien Enzyklopädie
Zur Navigation springen Zur Suche springen

Diese Vorlage erzeugt die URL für einen Link zum Compiler Explorer.

  • Dateinamen und Inhalt jeder einzelnen verwendeten Datei bilden ein Schlüssel-Wert-Paar, wobei der Dateiname der Schüssel und der Dateiinhalt der Wert ist. Dabei ist zu beachten, dass Punkte in den Dateinamen durch Unterstriche (_) zu ersetzen sind, da es sonst zu einer Fehlermeldung kommt. Pipes (|) trennen die einzelnen Dateien voneinader. Wichtig: Bei mehr als einer Datei muss die Hauptdatei example.c heißen bzw. CMakeLists.txt, wenn es sich im ein CMake-Projekt handelt.
  • Doppelte geschweifte Klammern ({{ und }}) sind innerhalb des Quellcodes nicht gestattet und müssen immer durch ein Leerzeichen voneinander getrennt werden ({ { bzw. } }), da sie sonst als Anfang oder Ende einer Wiki-Vorlage interpretiert werden. Deshalb muss auch nach eine schließenden geschweiften Klammer am Ende einer Datei ein Leerzeichen oder Zeilenumbruch gesetzt werden.
  • Pipes (|) müssen im Quellcode durch {{!}} ersetzt werden, da sie sonst als Feldtrenner interpertiert werden.
  • Für den Executor Frame gibt es folgende optionale Parameter, die ebenfalls Schlüssel-Wert-Paare bilden, die ebenfalls durch Pipes voneinander getrennt werden:
    • execArgsPanelShown gibt an, ob die Execution Arguments angezeigt werden (standardmäßig false)
    • execCompilationPanelShown gibt an, ob der Compiler und die Compiler Options angezeigt werden (standardmäßig true)
    • execCompiler enthält den Namen des verwendeten Compilers (standardmäßig "cg122"):
    • execCompilerOutputShown gibt an, ob der Compiler Output angezeigt wird (standardmäßig true)
    • execArgs enthält die Execution Arguments (standardmäßig ___)
    • execStdin enthät den Stdin (standardmäßig "" (ein leerer String))
    • execCompilerOptions wird für die Compiler-Optionen verwendet (standardmäßig "" (ein leerer String))
    • execStdinPanelShown gibt an, ob das Stdin angezeigt wird (standardmäßig false)
    • execLineWrap gibt an, ob es einen automatischen Zeilenumbruch gibt (standardmäßig false)
  • Wenn man CMake verwendet, sind die folgenden Optionen wichtig, die auch wieder Schlüssel-Wert-Paare bilden, die durch Pipes voneinander getrennt werden:
    • isCMakeProject gibt an, ob es sich im ein CMake-Projekt handelt (standardmäßig false)
    • CMakeArgs enhält die CMake Arguments (standardmäßig "" (ein leerer String))
    • CMakeCustomOutputFilename enthält den Namen der Ausgabedatei (standardmäßig "" (ein leerer String))
  • Folgende Bibliotheken sind dem Compiler Explorer bekannt und können ebenfalls verwendet werden, in dem man aus Schlüssel-Werte-Paare aus den Namen und der Versionsnummer (mögliche Versionen in Klammern angegeben) bildet: cs50 (910), hedley (v12), libuv (1370,1381), lua (535,540), nsimd(22-x86_64,22-arm,22-arm64,310-x86_64,310-arm,310-arm64), openssl (111c,111g), python (359,3610,376,381), simde (trunk), curl(7831), sqlite (3400).


Ein typischer Aufruf dieser Vorlage sieht wie folgt aus:
{{Vorlage:CompilerExplorerLink|main_c=#include <stdio.h>
void main(){
    printf("Hello World!");
} }}
Und erzeugt folgenden HTML-Code:
https://www.godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename:example.c,fontScale:14,fontUsePx:'0',j:1,lang:___c,selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:'%23include+%3Cstdio%2Eh%3E%0Avoid+main%28%29%7B%0A++++printf%28%22Hello+World!!%22%29%3B%0A%7D'),l:'5',n:'0',o:example.c,t:'0')),header:(),k:50,l:'4',m:100,n:'0',o:%27%27,s:0,t:'0'),(g:!((g:!((h:tree,i:(cmakeArgs:%27%27,compilerLanguageId:___c,customOutputFilename:%27%27,files:!((editorId:1,fileId:1,filename:example.c,g:%27%27,isIncluded:'0',isMainSource:'0',isOpen:'0',langId:%27%27)),isCMakeProject:'1',j:1,newFileId:2),l:'5',n:'0',o:'Tree+%231',t:'0')),header:(),l:'4',m:50,n:'0',o:%27%27,s:0,t:'0'),(g:!((h:executor,i:(argsPanelShown:'1',compilationPanelShown:'0',compiler:cg122,compilerOutShown:'0',execArgs:___,execStdin:%27%27,fontScale:14,fontUsePx:'0',j:1,lang:___c,libs:!(),options:%27%27,source:'1',stdinPanelShown:'1',tree:1,wrap:'1'),l:'5',n:'0',o:'Executor+(C,+Tree+%231)',t:'0')),header:(),k:50,l:'4',m:50,n:'0',o:%27%27,s:0,t:'0')),k:50,l:'3',n:'0',o:%27%27,s:0,t:'0')),l:'2',n:'0',o:%27%27,t:'0')),version:4
Diese URL kann man dann in einem Link einfügen: Hello World!


local CompilerExplorerLink = {}

-- Konvertiert Dezimal-Integer in Hexadezimal-Integer
local function hex(dec)
	local hb = math.floor(dec/16)
	local lb = dec%16
	if lb > 9 then
		lb = string.char(lb +55)
	end
	if hb > 9 then
		hb = string.char(hb +55)
	end
	return hb .. lb
end

-- Wandelt einen String so um, dass er innerhalb einer URL verwendet werden kann
-- Dabei werden Leerzeichen durch + ersetzt, andere Sonderzeichen durch ihren Hex-Code und
-- einfachen Anführungszeichen(') und Ausrufezeichen(!) ein Ausrufungszeichen vorgestellt
local function transform(code)
	local codestring = ""
	if code == nil then
		return "";
	end
	for i = 1, #code, 1 do
		local zeichen = code:byte(i)
		if (((zeichen >= 48) and (zeichen <=57)) or ((zeichen >= 65) and (zeichen <=90)) or ((zeichen >= 97) and (zeichen <=122))) then
			codestring = codestring .. string.char(zeichen)
		elseif zeichen == 39 then
			codestring = codestring .. "!'"
		elseif zeichen == 33 then
			codestring = codestring .. "!!"
		elseif zeichen == 95 then
			codestring = codestring .. "_"
		elseif zeichen == 32 then
			codestring = codestring .. "+"
		else
			codestring = codestring .. "%" .. hex(zeichen)
		end
	end
	return codestring
end

-- Wandelt eine 1 in eine 0 um und umgekehrt
-- Dies ist notwendig, da der Compiler Optionen eine 0 in der URL als true und eine 1 als false interpretiert
local function invert(a)
	if a == true then
		return "0"
	elseif a == false then
		return "1"
	else
		return nil
	end
end

-- Erzeugt eine URL zum Compiler Explorer
-- für jede "Datei" wird ein Eintrag in den Arrays "code" (der Inhalt der Datei)
-- und "filename" (der Name der Datei) benötigt.
-- Optionen können weggelassen werden. in diesem fall werden Standardwerte benutzt.
local function convert(source)
	-- Die Standardwerte der Optionen werden festgelegt
	local options = ""
	local compiler = "cg122"
	local execArgsPanelShown = "1"
	local execStdin = ""
	local execArgs = "___"
	local stdinPanelShown = "1"
	local wrap = "1"
	local compilerOutShown = "0"
	local compilationPanelShown = "0"
	local isCMakeProject = "1"
	local cmakeArgs = ""
	local customOutputFilename = ""
	local libs = {}
	
	-- falls vorhanden, werden die Optionen und Bibliotheken ausgewertet
	if (source.execArgsPanelShown == true) or (source.execArgsPanelShown == false) then
		execArgsPanelShown = invert(source.execArgsPanelShown)
	end
	if (source.execCompilationPanelShown == true) or (source.execCompilationPanelShown == false) then
		compilationPanelShown = invert(source.execCompilationPanelShown)
	end
	if source.execCompiler ~= nil then
		compiler = source.execCompiler
	end
	if (source.execCompilerOutputShown == true) or (source.execCompilerOutputShown == false) then
		compilerOutShown = invert(source.execCompilerOutputShown)
	end
	if source.execArgs ~= nil then
		execArgs = source.execArgs
	end
	if source.execStdin ~= nil then
		execStdin = source.execStdin
	end
	if source.execCompilerOptions ~= nil then
		options = source.execCompilerOptions
	end
	if (source.execStdinPanelShown == true) or (source.execStdinPanelShown == false) then
		stdinPanelShown = invert(source.execStdinPanelShown)
	end
	if (source.execLineWrap == true) or (source.execLineWrap == false) then
		wrap = invert(source.execLineWrap)
	end
	if (source.isCMakeProject == true) or (source.isCMakeProject == false) then
		isCMakeProject = invert(source.isCMakeProject)
	end
	if source.CMakeArgs ~= nil then
		cmakeArgs = source.CMakeArgs
	end
	if source.CMakeCustomOutputFilename ~= nil then
		customOutputFilename = source.CMakeCustomOutputFilename
	end
	if source.cs50 ~= nil then
		libs[#libs+1] = "(name:cs50,ver:'".. source.cs50 .. "')"
	end
	if source.hedley ~= nil then
		libs[#libs+1] = "(name:hedley,ver:".. source.hedley .. ")"
	end
	if source.libuv ~= nil then
		libs[#libs+1] = "(name:libuv,ver:'".. source.libuv .. "')"
	end
	if source.lua ~= nil then
		libs[#libs+1] = "(name:lua,ver:'".. source.lua .. "')"
	end
	if source.nsimd ~= nil then
		libs[#libs+1] = "(name:nsimd,ver:'".. source.nsimd .. "')"
	end
	if source.openssl ~= nil then
		libs[#libs+1] = "(name:openssl,ver:'".. source.openssl .. "')"
	end
	if source.python ~= nil then
		libs[#libs+1] = "(name:python,ver:'".. source.python .. "')"
	end
	if source.simde ~= nil then
		libs[#libs+1] = "(name:simde,ver:".. source.simde .. ")"
	end
	if source.curl ~= nil then
		libs[#libs+1] = "(name:curl,ver:'".. source.curl .. "')"
	end
	if source.sqlite ~= nil then
		libs[#libs+1] = "(name:sqlite,ver:'".. source.sqlite .. "')"
	end
	
	local name = {}
	local code = {}
	for key,value in pairs(source) do
		if key:find('_') ~= nil then
			key = string.gsub(key,'_','.')
			name[#name+1] = key
			code[#code+1] = value
		end
	end
	
	-- falls kein Dateiname oder Quellcode angegeben wurde,
	-- wird nur eine aus Schema und Host gebildete URL zurückgegeben
	if (#name == 0) or (#code == 0) then
		return "https://www.godbolt.org/"
	end

	-- Der Fragment-Teil der URL wird erzeugt
	local url = "g:!((g:!("
	
	-- da der Compiler Explorer verlangt, dass die Hauptdatei einen bestimmten Namen hat, wird dies hier sicher gestellt
	if #name == 1 then
		name[1] = "example.c"
		isCMakeProject = "1"
	end
	for i = 1, #name, 1 do
		url = url .. "(g:!((h:codeEditor,i:(filename:" .. name[i] .. ",fontScale:14,fontUsePx:'0',j:" .. i .. ",lang:___c,selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:'" .. transform(code[i]) .. "'),l:'5',n:'0',o:" .. name[i] .. ",t:'0')),header:(),k:" .. (100/(#name+1)) .. ",l:'4',m:100,n:'0',o:%27%27,s:0,t:'0'),"
	end

	url = url .. "(g:!((g:!((h:tree,i:(cmakeArgs:%27" .. cmakeArgs .. "%27,compilerLanguageId:___c,customOutputFilename:%27" .. customOutputFilename .. "%27,files:!(";
	
	for i = 1, #name, 1 do
		
		url = url .. "(editorId:" .. i .. ",fileId:" .. i .. ",filename:" .. name[i] .. ",g:%27%27,isIncluded:'0',isMainSource:'"
		
		if isCMakeProject == "0" then
			if 	name[i] == "CMakeLists.txt" then 
				url = url .. "0"
			else
				url = url .."1"
			end
		else
			if 	name[i] == "example.c" then 
				url = url .. "0"
			else
				url = url .."1"
			end
		end
		
		url = url .. "',isOpen:'0',langId:%27%27)"
		
		if i < #name then
			url = url .. ","
		end
	end
	
	url = url .. "),isCMakeProject:'".. isCMakeProject .. "',j:1,newFileId:" .. (#name+1) .. "),l:'5',n:'0',o:'Tree+%231',t:'0')),header:(),l:'4',m:50,n:'0',o:%27%27,s:0,t:'0'),(g:!((h:executor,i:(argsPanelShown:'" .. execArgsPanelShown .. "',compilationPanelShown:'" .. compilationPanelShown .. "',compiler:" .. compiler .. ",compilerOutShown:'" .. compilerOutShown .. "',execArgs:"
	if execArgs == "___" then
		url = url .. execArgs
	else
		url = url .. "'" .. transform(execArgs) .. "'"
	end
	url = url .. ",execStdin:%27" .. transform(execStdin) .."%27,fontScale:14,fontUsePx:'0',j:1,lang:___c,libs:!("
	if #libs > 0 then 
		for i = 1, #libs, 1 do
			url = url .. libs[i]
			if  i < #libs then
				url = url ..","
			end
		end
	end
	url = url .. "),options:%27" .. transform(options) .. "%27,source:'1',stdinPanelShown:'" .. stdinPanelShown .. "',tree:1,wrap:'" .. wrap .. "'),l:'5',n:'0',o:'Executor+(C,+Tree+%231)',t:'0')),header:(),k:50,l:'4',m:50,n:'0',o:%27%27,s:0,t:'0')),k:" .. (100/(#name+1)) .. ",l:'3',n:'0',o:%27%27,s:0,t:'0')),l:'2',n:'0',o:%27%27,t:'0')),version:4";

	-- Die vollständieg URL (Schema + Host + Fragment) wird zurückgegeben
	return "https://www.godbolt.org/#" .. url
end

-- Hauptprogramm: Erzeugt einen Link zum Compiler Explorer
-- Dateinamen und Quellcodes bilden ein Schlüssel-Wert-Paar,
-- wobei Punkte im Dateinamen durch '_' dargestellt werden müssen (so muss z.B. statt "main.c" "main_c" geschrieben werden)
-- Für den Executor Frame gibt es folgende Optionen, die ebenfalls Schlüssel-Wert-Paare bilden, aber angegeben werden müssen:
-- execCompilerOptions wird für die Compiler-Optionen verwendet (standardmäßig "")
-- execArgs enthält die Execution Arguments (standardmäßig ___)
-- execStdin enthät den Stdin (standardmäßig "")
-- execCompiler enthält den Namen des verwendeten Compilers (standardmäßig "cg122")
-- execArgsPanelShown gibt an, ob die Execution Arguments angezeigt werden (standardmäßig "0")
-- execStdinPanelShown gibt an, ob das Stdin angezeigt wird (standardmäßig "0")
-- execLineWrap gibt an, ob es einen automatischen Zeilenumbruch gibt (standardmäßig "0")
-- execCompilerOutputShown gibt an, ob der Compiler Output angezeigt wird (standardmäßig "1")
-- execCompilationPanelShown gibt an, ob der Compiler und die Compiler Options angezeigt werden (standardmäßig "1")
function CompilerExplorerLink.createLink(frame)
	local source = frame:getParent().args
	return tostring(convert(source))
end

return CompilerExplorerLink