Blockly review
This commit is contained in:
@@ -9,8 +9,16 @@ type
|
||||
// Callback-Prozedur, die von der DLL aufgerufen wird
|
||||
TLogIntegerCallback = procedure( AValue: Integer ); stdcall;
|
||||
|
||||
// Signatur der exportierten DLL-Funktion
|
||||
TStarteLogikProc = procedure( ALogCallback: TLogIntegerCallback ); stdcall;
|
||||
// NEU: Typ für die Host-Additionsfunktion
|
||||
THostAddIntegersCallback = function( ANum1: Integer; ANum2: Integer ): Integer; stdcall;
|
||||
|
||||
// NEUE, VEREINFACHTE Signatur der exportierten DLL-Funktion
|
||||
// Jetzt nur noch der Log-Callback und der neue Integer-Input-Parameter
|
||||
TStarteLogikProc = procedure(
|
||||
ALogIntProc: TLogIntegerCallback;
|
||||
AHostInputInteger: Integer;
|
||||
AHostAddIntegersProc: THostAddIntegersCallback // <--- Der neue Host-Unterprogramm-Parameter
|
||||
); stdcall;
|
||||
|
||||
type
|
||||
TLLVMRunner = class
|
||||
@@ -20,10 +28,12 @@ type
|
||||
class var
|
||||
FLog: TStrings;
|
||||
class procedure LogInteger( AValue: Integer ); static; stdcall;
|
||||
// NEU: Host-Unterprogramm-Implementierung
|
||||
class function HostAddIntegers( ANum1: Integer; ANum2: Integer ): Integer; static; stdcall;
|
||||
public
|
||||
constructor Create(const ADLLPath: string);
|
||||
destructor Destroy; override;
|
||||
procedure Execute;
|
||||
procedure Execute( AInputInteger: Integer );
|
||||
class property Log: TStrings read FLog write FLog;
|
||||
end;
|
||||
|
||||
@@ -50,19 +60,33 @@ begin
|
||||
inherited;
|
||||
end;
|
||||
|
||||
procedure TLLVMRunner.Execute;
|
||||
// NEUE Implementierung der Host-Funktion
|
||||
class function TLLVMRunner.HostAddIntegers( ANum1: Integer; ANum2: Integer ): Integer;
|
||||
begin
|
||||
if Assigned( FStarteLogik ) then
|
||||
// Führe die DLL-Funktion aus und übergebe unsere Callback-Methode
|
||||
FStarteLogik( LogInteger );
|
||||
Result := ANum1 + ANum2; // Die eigentliche Logik
|
||||
if Assigned(FLog) then
|
||||
FLog.Add(Format('DLL hat Host-Addition angefordert: %d + %d = %d', [ANum1, ANum2, Result]));
|
||||
end;
|
||||
|
||||
// Dies ist die Methode, die die DLL aufruft.
|
||||
// Sie muss "static" (class procedure) sein, um eine feste Adresse zu haben.
|
||||
// Die Execute-Methode muss angepasst werden, um den neuen Funktionszeiger zu übergeben
|
||||
procedure TLLVMRunner.Execute( AInputInteger: Integer );
|
||||
begin
|
||||
if Assigned( FStarteLogik ) then
|
||||
FStarteLogik(
|
||||
LogInteger,
|
||||
AInputInteger,
|
||||
HostAddIntegers // <--- Adresse der Host-Funktion übergeben
|
||||
);
|
||||
end;
|
||||
|
||||
// Dies ist die Methode, die die DLL für LogInteger aufruft.
|
||||
class procedure TLLVMRunner.LogInteger( AValue: Integer );
|
||||
begin
|
||||
if Assigned( FLOg ) then
|
||||
if Assigned( FLog ) then
|
||||
FLog.Add( Format( 'DLL logged integer: %d', [AValue] ) );
|
||||
end;
|
||||
|
||||
// Die Implementierungen von GetCurrentDateTimeUTCHost und GetComponentInZoneHost
|
||||
// wurden hier entfernt, da sie nicht mehr benötigt werden.
|
||||
|
||||
end.
|
||||
|
||||
@@ -30,7 +30,7 @@ object MainForm: TMainForm
|
||||
Size.PlatformDefault = False
|
||||
Text = 'Refresh'
|
||||
TextSettings.Trimming = None
|
||||
OnClick = LauchSiteButtonClick
|
||||
OnClick = RefreshButtonClick
|
||||
end
|
||||
object SaveWorkspaceButton: TSpeedButton
|
||||
Align = Left
|
||||
@@ -52,6 +52,19 @@ object MainForm: TMainForm
|
||||
TextSettings.Trimming = None
|
||||
OnClick = GenerateCodeButtonClick
|
||||
end
|
||||
object InputEdit: TEdit
|
||||
Touch.InteractiveGestures = [LongTap, DoubleTap]
|
||||
Align = Left
|
||||
TabOrder = 3
|
||||
Text = '1345'
|
||||
Position.X = 240.000000000000000000
|
||||
Position.Y = 10.000000000000000000
|
||||
Margins.Top = 10.000000000000000000
|
||||
Margins.Bottom = 10.000000000000000000
|
||||
Size.Width = 73.000000000000000000
|
||||
Size.Height = 20.000000000000000000
|
||||
Size.PlatformDefault = False
|
||||
end
|
||||
end
|
||||
end
|
||||
object Memo: TMemo
|
||||
|
||||
+76
-58
@@ -5,30 +5,29 @@ interface
|
||||
uses
|
||||
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
|
||||
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls, FMX.WebBrowser,
|
||||
Winapi.WebView2, FMX.Memo.Types, FMX.ScrollBox, FMX.Memo,
|
||||
Winapi.WebView2, FMX.Memo.Types, FMX.ScrollBox, FMX.Memo, FMX.Edit, FMX.StdActns,
|
||||
// FMX.Edit für TEdit, FMX.StdActns oft für Standardaktionen
|
||||
// Korrekte und vollständige Units
|
||||
System.IOUtils, System.Threading, System.Diagnostics, Winapi.Windows, LLVM.Runner;
|
||||
|
||||
const
|
||||
// Pfad zur HTML-Datei
|
||||
PATH_BLOCKLY = 'T:\Myc\Blockly\index.html';
|
||||
// Pfad zur HTML-Datei (BITTE HIER ANPASSEN!)
|
||||
PATH_BLOCKLY = 'T:\Myc\Blockly\index.html'; // <--- PRÜFEN UND ANPASSEN!
|
||||
|
||||
// Pfade zu den LLVM-Werkzeugen
|
||||
CLANG_PATH = 'clang.exe';
|
||||
LLD_PATH = 'lld-link.exe';
|
||||
// Pfade zu den LLVM-Werkzeugen (BITTE HIER ANPASSEN!)
|
||||
CLANG_PATH = 'clang.exe'; // <--- PRÜFEN UND ANPASSEN!
|
||||
LLD_PATH = 'lld-link.exe'; // <--- PRÜFEN UND ANPASSEN!
|
||||
|
||||
type
|
||||
TMainForm = class( TForm )
|
||||
WebBrowser1: TWebBrowser;
|
||||
SaveWorkspaceButton: TSpeedButton;
|
||||
GenerateCodeButton: TSpeedButton;
|
||||
Memo: TMemo;
|
||||
Splitter1: TSplitter;
|
||||
ToolBar1: TToolBar;
|
||||
RefreshButton: TSpeedButton;
|
||||
SaveWorkspaceButton: TSpeedButton;
|
||||
GenerateCodeButton: TSpeedButton;
|
||||
InputEdit: TEdit; // <--- Hinzugefügt (Optional für Beschriftung des InputEdit)
|
||||
procedure FormCreate( Sender: TObject );
|
||||
procedure FormDestroy( Sender: TObject );
|
||||
procedure LauchSiteButtonClick( Sender: TObject );
|
||||
procedure RefreshButtonClick( Sender: TObject );
|
||||
procedure SaveWorkspaceButtonClick( Sender: TObject );
|
||||
procedure GenerateCodeButtonClick( Sender: TObject );
|
||||
procedure WebBrowser1DidFinishLoad( ASender: TObject );
|
||||
@@ -85,7 +84,8 @@ end;
|
||||
procedure TMainForm.FormCreate( Sender: TObject );
|
||||
begin
|
||||
TLLVMRunner.Log := Memo.Lines;
|
||||
LauchSiteButtonClick(Self);
|
||||
// Optional: Standardwert für InputEdit
|
||||
InputEdit.Text := '123';
|
||||
end;
|
||||
|
||||
procedure TMainForm.FormDestroy( Sender: TObject );
|
||||
@@ -107,7 +107,7 @@ var
|
||||
cmdLine: string;
|
||||
buffer: TBytes;
|
||||
bytesRead: Cardinal;
|
||||
output: string;
|
||||
output, errors: string;
|
||||
begin
|
||||
// Pipe für StdOut und StdErr erstellen
|
||||
sa.nLength := SizeOf( TSecurityAttributes );
|
||||
@@ -186,7 +186,7 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMainForm.LauchSiteButtonClick( Sender: TObject );
|
||||
procedure TMainForm.RefreshButtonClick( Sender: TObject );
|
||||
begin
|
||||
WebBrowser1.Navigate( PATH_BLOCKLY );
|
||||
end;
|
||||
@@ -202,104 +202,122 @@ begin
|
||||
end;
|
||||
|
||||
procedure TMainForm.HandleWebMessage( const AMessage: string );
|
||||
var
|
||||
llFile, objFile, dllFile: string;
|
||||
compilerOutput: string;
|
||||
success: Boolean;
|
||||
InputInteger: Integer; // <--- Hinzugefügt
|
||||
begin
|
||||
Memo.Lines.Clear;
|
||||
Memo.Lines.Add( 'LLVM-Code empfangen. Starte Kompilierung...' );
|
||||
Memo.Lines.Add( AMessage );
|
||||
Memo.Lines.Add( '--------------------' );
|
||||
|
||||
// Wert aus dem InputEdit-Feld holen
|
||||
try
|
||||
InputInteger := StrToIntDef( InputEdit.Text, 0 ); // Standardwert 0, falls ungültig
|
||||
except
|
||||
InputInteger := 0;
|
||||
Memo.Lines.Add( 'WARNUNG: Ungültige Eingabe im InputEdit-Feld. Verwende 0.' );
|
||||
end;
|
||||
|
||||
TTask.Run(
|
||||
procedure
|
||||
var
|
||||
llFile, objFile, dllFile: string;
|
||||
compilerOutput: string;
|
||||
success: Boolean;
|
||||
llFileLocal, objFileLocal, dllFileLocal: string; // Lokale Variablen für den Task
|
||||
compilerOutputLocal: string;
|
||||
successLocal: Boolean;
|
||||
begin
|
||||
llFile := TPath.Combine( TPath.GetTempPath, 'poc.ll' );
|
||||
objFile := TPath.ChangeExtension( llFile, '.obj' );
|
||||
dllFile := TPath.ChangeExtension( llFile, '.dll' );
|
||||
success := False;
|
||||
llFileLocal := TPath.Combine( TPath.GetTempPath, 'poc.ll' );
|
||||
objFileLocal := TPath.ChangeExtension( llFileLocal, '.obj' );
|
||||
dllFileLocal := TPath.ChangeExtension( llFileLocal, '.dll' );
|
||||
successLocal := False;
|
||||
|
||||
try
|
||||
TFile.WriteAllText( llFile, AMessage );
|
||||
TFile.WriteAllText( llFileLocal, AMessage );
|
||||
|
||||
TThread.Queue( nil,
|
||||
procedure
|
||||
begin
|
||||
Memo.Lines.Add( 'Kompiliere zu Objektdatei...' );
|
||||
end );
|
||||
compilerOutput := ExecuteProcess( CLANG_PATH, Format( '-c "%s" -o "%s"', [llFile, objFile] ) );
|
||||
compilerOutputLocal := ExecuteProcess( CLANG_PATH, Format( '-c "%s" -o "%s"', [llFileLocal, objFileLocal] ) );
|
||||
TThread.Queue( nil,
|
||||
procedure
|
||||
begin
|
||||
Memo.Lines.Add( compilerOutput );
|
||||
Memo.Lines.Add( compilerOutputLocal );
|
||||
end );
|
||||
|
||||
if not TFile.Exists( objFile ) then
|
||||
if not TFile.Exists( objFileLocal ) then
|
||||
begin
|
||||
TThread.Queue( nil,
|
||||
TThread.Queue( nil,
|
||||
procedure
|
||||
begin
|
||||
Memo.Lines.Add( 'FEHLER: Kompilierung fehlgeschlagen. .obj-Datei nicht erstellt.' );
|
||||
end );
|
||||
Exit;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
TThread.Queue( nil,
|
||||
TThread.Queue( nil,
|
||||
procedure
|
||||
begin
|
||||
Memo.Lines.Add( 'Linke zu DLL...' );
|
||||
end );
|
||||
compilerOutput := ExecuteProcess( LLD_PATH, Format( '/dll /noentry "%s" /out:"%s"', [objFile, dllFile] ) );
|
||||
TThread.Queue( nil,
|
||||
compilerOutputLocal := ExecuteProcess( LLD_PATH, Format( '/dll /noentry "%s" /out:"%s"', [objFileLocal, dllFileLocal] ) );
|
||||
TThread.Queue( nil,
|
||||
procedure
|
||||
begin
|
||||
Memo.Lines.Add( compilerOutput );
|
||||
Memo.Lines.Add( compilerOutputLocal );
|
||||
end );
|
||||
|
||||
if TFile.Exists( dllFile ) then
|
||||
if TFile.Exists( dllFileLocal ) then
|
||||
begin
|
||||
success := True;
|
||||
TThread.Queue( nil,
|
||||
successLocal := True;
|
||||
TThread.Queue( nil,
|
||||
procedure
|
||||
begin
|
||||
Memo.Lines.Add( Format( 'ERFOLG: DLL wurde erstellt: %s', [dllFile] ) );
|
||||
Memo.Lines.Add( Format( 'ERFOLG: DLL wurde erstellt: %s', [dllFileLocal] ) );
|
||||
end );
|
||||
end
|
||||
else
|
||||
begin
|
||||
TThread.Queue( nil,
|
||||
TThread.Queue( nil,
|
||||
procedure
|
||||
begin
|
||||
Memo.Lines.Add( 'FEHLER: Linken fehlgeschlagen. .dll-Datei nicht erstellt.' );
|
||||
end );
|
||||
end;
|
||||
|
||||
finally
|
||||
if TFile.Exists( llFile ) then
|
||||
TFile.Delete( llFile );
|
||||
if TFile.Exists( objFile ) then
|
||||
TFile.Delete( objFile );
|
||||
if not success and TFile.Exists( dllFile ) then
|
||||
TFile.Delete( dllFile );
|
||||
finally
|
||||
if TFile.Exists( llFileLocal ) then
|
||||
TFile.Delete( llFileLocal );
|
||||
if TFile.Exists( objFileLocal ) then
|
||||
TFile.Delete( objFileLocal );
|
||||
if not successLocal and TFile.Exists( dllFileLocal ) then
|
||||
TFile.Delete( dllFileLocal );
|
||||
end;
|
||||
|
||||
Memo.Lines.Add( '---------------------------------------------------------' );
|
||||
Memo.Lines.Add( 'DLL wird aufgerufen:' );
|
||||
TThread.Queue( nil,
|
||||
procedure
|
||||
begin
|
||||
Memo.Lines.Add( '---------------------------------------------------------' );
|
||||
Memo.Lines.Add( 'DLL wird aufgerufen:' );
|
||||
|
||||
if TFile.Exists( dllFile ) then
|
||||
begin
|
||||
var
|
||||
Runner := TLLVMRunner.Create( dllFile );
|
||||
try
|
||||
Runner.Execute;
|
||||
finally
|
||||
Runner.Free;
|
||||
end;
|
||||
end;
|
||||
if TFile.Exists( dllFileLocal ) then
|
||||
begin
|
||||
var
|
||||
Runner := TLLVMRunner.Create( dllFileLocal );
|
||||
try
|
||||
Runner.Execute( InputInteger ); // <--- HIER DEN INPUT-PARAMETER ÜBERGEBEN
|
||||
finally
|
||||
Runner.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
Memo.Lines.Add( 'DLL beendet.' );
|
||||
Memo.CaretPosition := TCaretPosition.Create( Memo.Lines.Count-1, 1 );
|
||||
end );
|
||||
end );
|
||||
|
||||
Memo.Lines.Add( 'DLL beendet.' );
|
||||
end;
|
||||
|
||||
procedure TMainForm.WebBrowser1DidFinishLoad( ASender: TObject );
|
||||
|
||||
+98
-147
@@ -3,6 +3,16 @@
|
||||
let workspace;
|
||||
const isWebView = window.chrome && window.chrome.webview;
|
||||
|
||||
// === Debounce-Funktion ===
|
||||
let debounceTimeout;
|
||||
const debounce = (func, delay) => {
|
||||
return function(...args) {
|
||||
const context = this;
|
||||
clearTimeout(debounceTimeout);
|
||||
debounceTimeout = setTimeout(() => func.apply(context, args), delay);
|
||||
};
|
||||
};
|
||||
|
||||
// === LLVM-IR-GENERATOR (FINALE VERSION) ===
|
||||
Blockly.LLVM = new Blockly.Generator('LLVM');
|
||||
Blockly.LLVM.ORDER_ATOMIC = 0;
|
||||
@@ -27,50 +37,31 @@
|
||||
"helpUrl": ""
|
||||
},
|
||||
{
|
||||
"type": "current_datetime_utc",
|
||||
"message0": "Aktueller UTC-Zeitstempel",
|
||||
"output": "DateTimeUTC",
|
||||
"colour": 120,
|
||||
"tooltip": "Liefert den aktuellen Zeitstempel in UTC.",
|
||||
"type": "host_get_integer_input",
|
||||
"message0": "Hole Integer-Eingabe von Host",
|
||||
"output": "Number",
|
||||
"colour": 290,
|
||||
"tooltip": "Liefert einen Integer-Wert, der vom Delphi-Host übergeben wurde.",
|
||||
"helpUrl": ""
|
||||
},
|
||||
{
|
||||
"type": "get_datetime_component",
|
||||
"message0": "Hole %1 von Zeitstempel %2 in Zeitzone %3",
|
||||
"type": "host_add_integers",
|
||||
"message0": "Addiere %1 und %2 auf Host",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_dropdown",
|
||||
"name": "COMPONENT",
|
||||
"options": [
|
||||
["Jahr", "YEAR"],
|
||||
["Monat", "MONTH"],
|
||||
["Tag", "DAY"],
|
||||
["Stunde", "HOUR"],
|
||||
["Minute", "MINUTE"],
|
||||
["Sekunde", "SECOND"]
|
||||
]
|
||||
"type": "input_value",
|
||||
"name": "NUM1",
|
||||
"check": "Number"
|
||||
},
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "DATETIME",
|
||||
"check": "DateTimeUTC"
|
||||
},
|
||||
{
|
||||
"type": "field_dropdown",
|
||||
"name": "TIMEZONE",
|
||||
"options": [
|
||||
["UTC", "UTC"],
|
||||
["ET (New York)", "ET"],
|
||||
["WET (London)", "WET"],
|
||||
["CET (Frankfurt)", "CET"],
|
||||
["AEST (Sydney)", "AEST"],
|
||||
["JST (Tokyo)", "JST"]
|
||||
]
|
||||
"name": "NUM2",
|
||||
"check": "Number"
|
||||
}
|
||||
],
|
||||
"output": "Number",
|
||||
"colour": 120,
|
||||
"tooltip": "Extrahiert eine Komponente (Jahr, Monat, etc.) aus einem UTC-Zeitstempel und konvertiert sie in die angegebene Zeitzone.",
|
||||
"colour": 290, // Host-Farbe
|
||||
"tooltip": "Addiert zwei Integer-Werte unter Verwendung einer Host-Funktion.",
|
||||
"helpUrl": ""
|
||||
}
|
||||
]);
|
||||
@@ -83,63 +74,58 @@
|
||||
};
|
||||
|
||||
Blockly.LLVM.workspaceToCode = function(workspace) {
|
||||
Blockly.LLVM.regCounter = 1;
|
||||
Blockly.LLVM.generatedStrings = new Set();
|
||||
Blockly.LLVM.globalDeclarations = '';
|
||||
Blockly.LLVM.functionDeclarations = '';
|
||||
Blockly.LLVM.regCounter = 1;
|
||||
Blockly.LLVM.generatedStrings = new Set();
|
||||
Blockly.LLVM.globalDeclarations = '';
|
||||
Blockly.LLVM.functionDeclarations = '';
|
||||
|
||||
const allVariables = workspace.getAllVariables();
|
||||
let varAllocations = '';
|
||||
if (allVariables.length > 0) {
|
||||
allVariables.forEach(v => {
|
||||
const safeVarName = v.name.replace(/ /g, '_');
|
||||
varAllocations += ` @${safeVarName} = common global i32 0\n`;
|
||||
});
|
||||
}
|
||||
const allVariables = workspace.getAllVariables();
|
||||
let varAllocations = '';
|
||||
if (allVariables.length > 0) {
|
||||
allVariables.forEach(v => {
|
||||
const safeVarName = v.name.replace(/ /g, '_');
|
||||
varAllocations += ` @${safeVarName} = common global i32 0\n`;
|
||||
});
|
||||
}
|
||||
|
||||
const topBlock = workspace.getTopBlocks(true)[0];
|
||||
const code = topBlock ? Blockly.LLVM.blockToCode(topBlock) : '';
|
||||
const topBlock = workspace.getTopBlocks(true)[0];
|
||||
const code = topBlock ? Blockly.LLVM.blockToCode(topBlock) : '';
|
||||
|
||||
let moduleCode = `target triple = "x86_64-pc-windows-msvc"\n\n`;
|
||||
let moduleCode = `target triple = "x86_64-pc-windows-msvc"\n\n`;
|
||||
|
||||
moduleCode += `; Definition des DateTimeUTC-Structs\n`;
|
||||
moduleCode += `%struct.DateTimeUTC = type { i32, i32, i32, i32, i32, i32 }\n\n`;
|
||||
// NEU: Deklaration der Host-Funktion (Signatur, die der Host implementiert)
|
||||
moduleCode += `; Host Provided Function Declarations\n`;
|
||||
moduleCode += `declare i32 @host_add_integers_func(i32, i32)\n\n`; // Beispiel: Funktion, die zwei i32 nimmt und i32 zurückgibt
|
||||
|
||||
if (Blockly.LLVM.globalDeclarations) {
|
||||
moduleCode += '; Global string constants for timezones\n';
|
||||
moduleCode += Blockly.LLVM.globalDeclarations + '\n';
|
||||
}
|
||||
if (Blockly.LLVM.functionDeclarations) {
|
||||
moduleCode += Blockly.LLVM.functionDeclarations + '\n';
|
||||
}
|
||||
|
||||
moduleCode += `; Host Callback Declarations\n`;
|
||||
moduleCode += `declare void @get_current_datetime_utc(%struct.DateTimeUTC*)\n`;
|
||||
moduleCode += `declare i32 @get_datetime_component_in_zone(%struct.DateTimeUTC*, i32, i8*)\n`;
|
||||
|
||||
if (Blockly.LLVM.functionDeclarations) {
|
||||
moduleCode += Blockly.LLVM.functionDeclarations + '\n';
|
||||
}
|
||||
|
||||
moduleCode += `@gLogIntProc = common global void (i32)* null\n`;
|
||||
moduleCode += `@gGetCurrentDateTimeUTCProc = common global void (%struct.DateTimeUTC*)* null\n`;
|
||||
moduleCode += `@gGetComponentInZoneProc = common global i32 (%struct.DateTimeUTC*, i32, i8*)* null\n\n`;
|
||||
moduleCode += `@gLogIntProc = common global void (i32)* null\n`;
|
||||
// NEU: Globaler Zeiger für die Host-Additionsfunktion
|
||||
moduleCode += `@gHostAddIntegersProc = common global i32 (i32, i32)* null\n\n`;
|
||||
|
||||
|
||||
if (varAllocations) {
|
||||
moduleCode += '; Global Variables\n';
|
||||
moduleCode += varAllocations + '\n';
|
||||
}
|
||||
if (varAllocations) {
|
||||
moduleCode += '; Global Variables\n';
|
||||
moduleCode += varAllocations + '\n';
|
||||
}
|
||||
|
||||
moduleCode += 'define dllexport void @StarteLogik(void (i32)* %LogIntProc, void (%struct.DateTimeUTC*)* %GetCurrentDateTimeUTCProc, i32 (%struct.DateTimeUTC*, i32, i8*)* %GetComponentInZoneProc) {\n';
|
||||
moduleCode += 'entry:\n';
|
||||
moduleCode += ' store void (i32)* %LogIntProc, void (i32)** @gLogIntProc\n';
|
||||
moduleCode += ' store void (%struct.DateTimeUTC*)* %GetCurrentDateTimeUTCProc, void (%struct.DateTimeUTC*)** @gGetCurrentDateTimeUTCProc\n';
|
||||
moduleCode += ' store i32 (%struct.DateTimeUTC*, i32, i8*)* %GetComponentInZoneProc, i32 (%struct.DateTimeUTC*, i32, i8*)** @gGetComponentInZoneProc\n\n';
|
||||
// StarteLogik-Signatur erweitern, um den neuen Funktionszeiger-Parameter zu akzeptieren
|
||||
moduleCode += 'define dllexport void @StarteLogik(void (i32)* %LogIntProc, i32 %hostInputInteger, i32 (i32, i32)* %HostAddIntegersProc) {\n';
|
||||
moduleCode += 'entry:\n';
|
||||
moduleCode += ' store void (i32)* %LogIntProc, void (i32)** @gLogIntProc\n';
|
||||
// NEU: Den übergebenen Funktionszeiger in den globalen Zeiger speichern
|
||||
moduleCode += ' store i32 (i32, i32)* %HostAddIntegersProc, i32 (i32, i32)** @gHostAddIntegersProc\n\n';
|
||||
|
||||
moduleCode += code.split('\n').map(line => line ? ' ' + line : '').join('\n');
|
||||
moduleCode += '\n ret void\n';
|
||||
moduleCode += '}\n';
|
||||
return moduleCode;
|
||||
|
||||
moduleCode += code.split('\n').map(line => line ? ' ' + line : '').join('\n');
|
||||
moduleCode += '\n ret void\n';
|
||||
moduleCode += '}\n';
|
||||
return moduleCode;
|
||||
};
|
||||
|
||||
|
||||
Blockly.LLVM.scrub_ = function(block, code) {
|
||||
const nextBlock = block.nextConnection && block.nextConnection.targetBlock();
|
||||
const nextCode = nextBlock ? Blockly.LLVM.blockToCode(nextBlock) : '';
|
||||
@@ -196,54 +182,26 @@
|
||||
return code;
|
||||
};
|
||||
|
||||
Blockly.LLVM.forBlock['current_datetime_utc'] = function(block) {
|
||||
const structPtrReg = Blockly.LLVM.newReg();
|
||||
const getUtcProcPtrReg = Blockly.LLVM.newReg();
|
||||
|
||||
let code = `
|
||||
${structPtrReg} = alloca %struct.DateTimeUTC, align 8
|
||||
${getUtcProcPtrReg} = load void (%struct.DateTimeUTC*)*, void (%struct.DateTimeUTC*)** @gGetCurrentDateTimeUTCProc\n
|
||||
call void ${getUtcProcPtrReg}(%struct.DateTimeUTC* ${structPtrReg})
|
||||
`;
|
||||
|
||||
return [code + structPtrReg, Blockly.LLVM.ORDER_ATOMIC];
|
||||
Blockly.LLVM.forBlock['host_get_integer_input'] = function(block) {
|
||||
const inputParamReg = '%hostInputInteger';
|
||||
return [inputParamReg, Blockly.LLVM.ORDER_ATOMIC];
|
||||
};
|
||||
|
||||
Blockly.LLVM.forBlock['get_datetime_component'] = function(block) {
|
||||
const datetimePtr = Blockly.LLVM.valueToCode(block, 'DATETIME', Blockly.LLVM.ORDER_ATOMIC) || 'null';
|
||||
const component = block.getFieldValue('COMPONENT');
|
||||
const timezone = block.getFieldValue('TIMEZONE');
|
||||
Blockly.LLVM.forBlock['host_add_integers'] = function(block) {
|
||||
const num1 = Blockly.LLVM.valueToCode(block, 'NUM1', Blockly.LLVM.ORDER_ATOMIC) || '0';
|
||||
const num2 = Blockly.LLVM.valueToCode(block, 'NUM2', Blockly.LLVM.ORDER_ATOMIC) || '0';
|
||||
|
||||
const timezoneGlobalName = `@.str.timezone_${timezone.replace(/[^a-zA-Z0-9_]/g, '')}`;
|
||||
const timezoneStringLength = new TextEncoder().encode(timezone).length + 1;
|
||||
const funcPtrReg = Blockly.LLVM.newReg();
|
||||
const resultReg = Blockly.LLVM.newReg();
|
||||
|
||||
if (!Blockly.LLVM.generatedStrings.has(timezoneGlobalName)) {
|
||||
Blockly.LLVM.globalDeclarations += `${timezoneGlobalName} = private unnamed_addr constant [${timezoneStringLength} x i8] c"${timezone}\\00", align 1\n`;
|
||||
Blockly.LLVM.generatedStrings.add(timezoneGlobalName);
|
||||
}
|
||||
let code = '';
|
||||
// Lade den globalen Funktionszeiger, der vom Host gesetzt wird
|
||||
code += `${funcPtrReg} = load i32 (i32, i32)*, i32 (i32, i32)** @gHostAddIntegersProc\n`;
|
||||
// Rufe die Host-Funktion auf
|
||||
code += `${resultReg} = call i32 ${funcPtrReg}(i32 ${num1}, i32 ${num2})\n`;
|
||||
|
||||
let componentIndex = 0;
|
||||
switch (component) {
|
||||
case 'YEAR': componentIndex = 0; break;
|
||||
case 'MONTH': componentIndex = 1; break;
|
||||
case 'DAY': componentIndex = 2; break;
|
||||
case 'HOUR': componentIndex = 3; break;
|
||||
case 'MINUTE': componentIndex = 4; break;
|
||||
case 'SECOND': componentIndex = 5; break;
|
||||
}
|
||||
|
||||
const resultReg = Blockly.LLVM.newReg();
|
||||
const timezoneStringPtrReg = Blockly.LLVM.newReg();
|
||||
const getComponentProcPtrReg = Blockly.LLVM.newReg();
|
||||
|
||||
let code = `
|
||||
${timezoneStringPtrReg} = getelementptr inbounds [${timezoneStringLength} x i8], [${timezoneStringLength} x i8]* ${timezoneGlobalName}, i64 0, i64 0
|
||||
${getComponentProcPtrReg} = load i32 (%struct.DateTimeUTC*, i32, i8*)*, i32 (%struct.DateTimeUTC*, i32, i8*)** @gGetComponentInZoneProc\n
|
||||
${resultReg} = call i32 ${getComponentProcPtrReg}(%struct.DateTimeUTC* ${datetimePtr}, i32 ${componentIndex}, i8* ${timezoneStringPtrReg})
|
||||
`;
|
||||
|
||||
return [code + resultReg, Blockly.LLVM.ORDER_ATOMIC];
|
||||
};
|
||||
return [code + resultReg, Blockly.LLVM.ORDER_ATOMIC];
|
||||
};
|
||||
|
||||
const toolboxXml = `
|
||||
<xml>
|
||||
@@ -256,12 +214,10 @@
|
||||
</category>
|
||||
<category name="Host" colour="290">
|
||||
<block type="host_log_integer"></block>
|
||||
<block type="host_get_integer_input"></block>
|
||||
<block type="host_add_integers"></block>
|
||||
</category>
|
||||
<category name="Datum & Zeit" colour="120">
|
||||
<block type="current_datetime_utc"></block>
|
||||
<block type="get_datetime_component"></block>
|
||||
</category>
|
||||
</xml>`;
|
||||
</xml>`; // <--- DAS FEHLENDE BACKTICK HIER IST DIE LÖSUNG!
|
||||
|
||||
workspace = Blockly.inject('blocklyDiv', {
|
||||
toolbox: toolboxXml,
|
||||
@@ -310,20 +266,23 @@
|
||||
console.log(code);
|
||||
}
|
||||
}
|
||||
window.generateAndPostCode = generateAndPostCode; // Exponiere für Buttons
|
||||
window.generateAndPostCode = generateAndPostCode;
|
||||
|
||||
window.saveWorkspace = function() {
|
||||
// Diese Funktion kann den Arbeitsbereich manuell speichern und einen Alert anzeigen
|
||||
const state = Blockly.serialization.workspaces.save(workspace);
|
||||
localStorage.setItem('blocklyWorkspace', JSON.stringify(state));
|
||||
alert('Arbeitsbereich gespeichert!');
|
||||
}
|
||||
window.loadWorkspace = function() { const stateString = localStorage.getItem('blocklyWorkspace'); if (!stateString) { return; } const state = JSON.parse(stateString); Blockly.serialization.workspaces.load(state, workspace); }
|
||||
|
||||
// --- Dynamische Code-Aktualisierung / Posten (ruft generateAndPostCode auf) ---
|
||||
// --- Debounced-Funktion für die Code-Generierung ---
|
||||
// Die Generierung wird erst 500ms nach der letzten Änderung im Arbeitsbereich ausgelöst.
|
||||
const debouncedGenerateAndPostCode = debounce(generateAndPostCode, 500);
|
||||
|
||||
// --- Dynamische Code-Aktualisierung / Posten (ruft debouncedGenerateAndPostCode auf) ---
|
||||
function handleCodeUpdate(event) {
|
||||
if (event.isUiEvent) return; // Nur Änderungen durch Blöcke, nicht UI-Interaktionen
|
||||
generateAndPostCode(); // Dies triggert jetzt sowohl Speichern als auch Generieren/Posten
|
||||
if (event.isUiEvent) return;
|
||||
debouncedGenerateAndPostCode();
|
||||
}
|
||||
workspace.addChangeListener(handleCodeUpdate);
|
||||
loadWorkspace();
|
||||
@@ -332,32 +291,24 @@
|
||||
const outputContainer = document.getElementById('outputContainer');
|
||||
const blocklyDiv = document.getElementById('blocklyDiv');
|
||||
|
||||
if (isWebView) {
|
||||
// Wenn im WebView, outputContainer bleibt ausgeblendet (CSS default: display: none;)
|
||||
// BlocklyDiv die gesamte Breite geben
|
||||
if (isWebView) {
|
||||
blocklyDiv.style.width = '100%';
|
||||
// Blockly-Workspace an neue Größe anpassen, mit einer kleinen Verzögerung für WebView
|
||||
setTimeout(function() {
|
||||
Blockly.svgResize(workspace);
|
||||
}, 100); // 100ms Verzögerung, kann bei Bedarf angepasst werden.
|
||||
Blockly.svgResize(workspace);
|
||||
}, 100);
|
||||
|
||||
// Entferne die Schaltflächen "Speichern" und "Laden", wenn im WebView
|
||||
// da die Speicherung automatisch erfolgt und der Host den Code direkt bekommt.
|
||||
const buttonBar = document.querySelector('.button-bar');
|
||||
if (buttonBar) {
|
||||
const saveButton = buttonBar.querySelector('button[onclick="saveWorkspace()"]');
|
||||
const loadButton = buttonBar.querySelector('button[onclick="loadWorkspace()"]');
|
||||
if (saveButton) saveButton.remove();
|
||||
if (loadButton) loadButton.remove();
|
||||
// Eventuell auch den "Code generieren und posten" Button entfernen, da es automatisch passiert
|
||||
// oder umbenennen in "Aktualisieren" / "Neu senden"
|
||||
const postButton = buttonBar.querySelector('button[onclick="generateAndPostCode()"]');
|
||||
if (postButton) postButton.remove(); // Oder postButton.textContent = "Code senden";
|
||||
if (postButton) postButton.remove();
|
||||
}
|
||||
|
||||
} else {
|
||||
// Wenn NICHT im WebView, outputContainer einblenden und Splitter-Logik initialisieren
|
||||
outputContainer.style.display = 'flex'; // Einblenden als Flex-Container
|
||||
outputContainer.style.display = 'flex';
|
||||
|
||||
const outputArea = document.getElementById('outputArea');
|
||||
const splitter = document.getElementById('splitter');
|
||||
@@ -410,12 +361,12 @@
|
||||
document.body.style.cursor = '';
|
||||
});
|
||||
|
||||
// Initiales Resize für Nicht-WebView: Setzt die initialen Breiten
|
||||
// Basierend auf der Startbreite der outputArea (500px)
|
||||
const initialOutputAreaWidth = 500; // Aus CSS initialer Wert
|
||||
const initialOutputAreaWidth = 500;
|
||||
const totalFlexContentWidth = container.offsetWidth - splitter.offsetWidth - parseInt(window.getComputedStyle(outputArea).marginLeft);
|
||||
let initialBlocklyWidth = totalFlexContentWidth - initialOutputAreaWidth;
|
||||
|
||||
const minBlocklyWidth = parseFloat(window.getComputedStyle(blocklyDiv).minWidth);
|
||||
|
||||
if (initialBlocklyWidth < minBlocklyWidth) {
|
||||
initialBlocklyWidth = minBlocklyWidth;
|
||||
outputArea.style.width = `${totalFlexContentWidth - initialBlocklyWidth}px`;
|
||||
|
||||
Reference in New Issue
Block a user