Blockly review

This commit is contained in:
Michael Schimmel
2025-06-11 01:33:52 +02:00
parent b8a530254d
commit 3cfca1f167
4 changed files with 248 additions and 242 deletions
+48 -24
View File
@@ -9,24 +9,34 @@ type
// Callback-Prozedur, die von der DLL aufgerufen wird // Callback-Prozedur, die von der DLL aufgerufen wird
TLogIntegerCallback = procedure( AValue: Integer ); stdcall; TLogIntegerCallback = procedure( AValue: Integer ); stdcall;
// Signatur der exportierten DLL-Funktion // NEU: Typ für die Host-Additionsfunktion
TStarteLogikProc = procedure( ALogCallback: TLogIntegerCallback ); stdcall; 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 type
TLLVMRunner = class TLLVMRunner = class
private private
FModule: HMODULE; FModule: HMODULE;
FStarteLogik: TStarteLogikProc; FStarteLogik: TStarteLogikProc;
class var class var
FLog: TStrings; FLog: TStrings;
class procedure LogInteger( AValue: Integer ); static; stdcall; class procedure LogInteger( AValue: Integer ); static; stdcall;
public // NEU: Host-Unterprogramm-Implementierung
constructor Create(const ADLLPath: string); class function HostAddIntegers( ANum1: Integer; ANum2: Integer ): Integer; static; stdcall;
destructor Destroy; override; public
procedure Execute; constructor Create(const ADLLPath: string);
class property Log: TStrings read FLog write FLog; destructor Destroy; override;
end; procedure Execute( AInputInteger: Integer );
class property Log: TStrings read FLog write FLog;
end;
implementation implementation
{ TLLVMRunner } { TLLVMRunner }
@@ -50,19 +60,33 @@ begin
inherited; inherited;
end; end;
procedure TLLVMRunner.Execute; // NEUE Implementierung der Host-Funktion
begin class function TLLVMRunner.HostAddIntegers( ANum1: Integer; ANum2: Integer ): Integer;
if Assigned( FStarteLogik ) then begin
// Führe die DLL-Funktion aus und übergebe unsere Callback-Methode Result := ANum1 + ANum2; // Die eigentliche Logik
FStarteLogik( LogInteger ); if Assigned(FLog) then
FLog.Add(Format('DLL hat Host-Addition angefordert: %d + %d = %d', [ANum1, ANum2, Result]));
end;
// 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; end;
// Dies ist die Methode, die die DLL aufruft. // Dies ist die Methode, die die DLL für LogInteger aufruft.
// Sie muss "static" (class procedure) sein, um eine feste Adresse zu haben.
class procedure TLLVMRunner.LogInteger( AValue: Integer ); class procedure TLLVMRunner.LogInteger( AValue: Integer );
begin begin
if Assigned( FLOg ) then if Assigned( FLog ) then
FLog.Add( Format( 'DLL logged integer: %d', [AValue] ) ); FLog.Add( Format( 'DLL logged integer: %d', [AValue] ) );
end; end;
// Die Implementierungen von GetCurrentDateTimeUTCHost und GetComponentInZoneHost
// wurden hier entfernt, da sie nicht mehr benötigt werden.
end. end.
+14 -1
View File
@@ -30,7 +30,7 @@ object MainForm: TMainForm
Size.PlatformDefault = False Size.PlatformDefault = False
Text = 'Refresh' Text = 'Refresh'
TextSettings.Trimming = None TextSettings.Trimming = None
OnClick = LauchSiteButtonClick OnClick = RefreshButtonClick
end end
object SaveWorkspaceButton: TSpeedButton object SaveWorkspaceButton: TSpeedButton
Align = Left Align = Left
@@ -52,6 +52,19 @@ object MainForm: TMainForm
TextSettings.Trimming = None TextSettings.Trimming = None
OnClick = GenerateCodeButtonClick OnClick = GenerateCodeButtonClick
end 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
end end
object Memo: TMemo object Memo: TMemo
+76 -58
View File
@@ -5,30 +5,29 @@ interface
uses uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, 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, 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 // Korrekte und vollständige Units
System.IOUtils, System.Threading, System.Diagnostics, Winapi.Windows, LLVM.Runner; System.IOUtils, System.Threading, System.Diagnostics, Winapi.Windows, LLVM.Runner;
const const
// Pfad zur HTML-Datei // Pfad zur HTML-Datei (BITTE HIER ANPASSEN!)
PATH_BLOCKLY = 'T:\Myc\Blockly\index.html'; PATH_BLOCKLY = 'T:\Myc\Blockly\index.html'; // <--- PRÜFEN UND ANPASSEN!
// Pfade zu den LLVM-Werkzeugen // Pfade zu den LLVM-Werkzeugen (BITTE HIER ANPASSEN!)
CLANG_PATH = 'clang.exe'; CLANG_PATH = 'clang.exe'; // <--- PRÜFEN UND ANPASSEN!
LLD_PATH = 'lld-link.exe'; LLD_PATH = 'lld-link.exe'; // <--- PRÜFEN UND ANPASSEN!
type type
TMainForm = class( TForm ) TMainForm = class( TForm )
WebBrowser1: TWebBrowser; WebBrowser1: TWebBrowser;
SaveWorkspaceButton: TSpeedButton;
GenerateCodeButton: TSpeedButton;
Memo: TMemo; Memo: TMemo;
Splitter1: TSplitter; InputEdit: TEdit; // <--- Hinzugefügt (Optional für Beschriftung des InputEdit)
ToolBar1: TToolBar;
RefreshButton: TSpeedButton;
SaveWorkspaceButton: TSpeedButton;
GenerateCodeButton: TSpeedButton;
procedure FormCreate( Sender: TObject ); procedure FormCreate( Sender: TObject );
procedure FormDestroy( Sender: TObject ); procedure FormDestroy( Sender: TObject );
procedure LauchSiteButtonClick( Sender: TObject ); procedure RefreshButtonClick( Sender: TObject );
procedure SaveWorkspaceButtonClick( Sender: TObject ); procedure SaveWorkspaceButtonClick( Sender: TObject );
procedure GenerateCodeButtonClick( Sender: TObject ); procedure GenerateCodeButtonClick( Sender: TObject );
procedure WebBrowser1DidFinishLoad( ASender: TObject ); procedure WebBrowser1DidFinishLoad( ASender: TObject );
@@ -85,7 +84,8 @@ end;
procedure TMainForm.FormCreate( Sender: TObject ); procedure TMainForm.FormCreate( Sender: TObject );
begin begin
TLLVMRunner.Log := Memo.Lines; TLLVMRunner.Log := Memo.Lines;
LauchSiteButtonClick(Self); // Optional: Standardwert für InputEdit
InputEdit.Text := '123';
end; end;
procedure TMainForm.FormDestroy( Sender: TObject ); procedure TMainForm.FormDestroy( Sender: TObject );
@@ -107,7 +107,7 @@ var
cmdLine: string; cmdLine: string;
buffer: TBytes; buffer: TBytes;
bytesRead: Cardinal; bytesRead: Cardinal;
output: string; output, errors: string;
begin begin
// Pipe für StdOut und StdErr erstellen // Pipe für StdOut und StdErr erstellen
sa.nLength := SizeOf( TSecurityAttributes ); sa.nLength := SizeOf( TSecurityAttributes );
@@ -186,7 +186,7 @@ begin
end; end;
end; end;
procedure TMainForm.LauchSiteButtonClick( Sender: TObject ); procedure TMainForm.RefreshButtonClick( Sender: TObject );
begin begin
WebBrowser1.Navigate( PATH_BLOCKLY ); WebBrowser1.Navigate( PATH_BLOCKLY );
end; end;
@@ -202,104 +202,122 @@ begin
end; end;
procedure TMainForm.HandleWebMessage( const AMessage: string ); procedure TMainForm.HandleWebMessage( const AMessage: string );
var
llFile, objFile, dllFile: string;
compilerOutput: string;
success: Boolean;
InputInteger: Integer; // <--- Hinzugefügt
begin begin
Memo.Lines.Clear; Memo.Lines.Clear;
Memo.Lines.Add( 'LLVM-Code empfangen. Starte Kompilierung...' ); Memo.Lines.Add( 'LLVM-Code empfangen. Starte Kompilierung...' );
Memo.Lines.Add( AMessage ); Memo.Lines.Add( AMessage );
Memo.Lines.Add( '--------------------' ); 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( TTask.Run(
procedure procedure
var var
llFile, objFile, dllFile: string; llFileLocal, objFileLocal, dllFileLocal: string; // Lokale Variablen für den Task
compilerOutput: string; compilerOutputLocal: string;
success: Boolean; successLocal: Boolean;
begin begin
llFile := TPath.Combine( TPath.GetTempPath, 'poc.ll' ); llFileLocal := TPath.Combine( TPath.GetTempPath, 'poc.ll' );
objFile := TPath.ChangeExtension( llFile, '.obj' ); objFileLocal := TPath.ChangeExtension( llFileLocal, '.obj' );
dllFile := TPath.ChangeExtension( llFile, '.dll' ); dllFileLocal := TPath.ChangeExtension( llFileLocal, '.dll' );
success := False; successLocal := False;
try try
TFile.WriteAllText( llFile, AMessage ); TFile.WriteAllText( llFileLocal, AMessage );
TThread.Queue( nil, TThread.Queue( nil,
procedure procedure
begin begin
Memo.Lines.Add( 'Kompiliere zu Objektdatei...' ); Memo.Lines.Add( 'Kompiliere zu Objektdatei...' );
end ); 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, TThread.Queue( nil,
procedure procedure
begin begin
Memo.Lines.Add( compilerOutput ); Memo.Lines.Add( compilerOutputLocal );
end ); end );
if not TFile.Exists( objFile ) then if not TFile.Exists( objFileLocal ) then
begin begin
TThread.Queue( nil, TThread.Queue( nil,
procedure procedure
begin begin
Memo.Lines.Add( 'FEHLER: Kompilierung fehlgeschlagen. .obj-Datei nicht erstellt.' ); Memo.Lines.Add( 'FEHLER: Kompilierung fehlgeschlagen. .obj-Datei nicht erstellt.' );
end ); end );
Exit; Exit;
end; end;
TThread.Queue( nil, TThread.Queue( nil,
procedure procedure
begin begin
Memo.Lines.Add( 'Linke zu DLL...' ); Memo.Lines.Add( 'Linke zu DLL...' );
end ); end );
compilerOutput := ExecuteProcess( LLD_PATH, Format( '/dll /noentry "%s" /out:"%s"', [objFile, dllFile] ) ); compilerOutputLocal := ExecuteProcess( LLD_PATH, Format( '/dll /noentry "%s" /out:"%s"', [objFileLocal, dllFileLocal] ) );
TThread.Queue( nil, TThread.Queue( nil,
procedure procedure
begin begin
Memo.Lines.Add( compilerOutput ); Memo.Lines.Add( compilerOutputLocal );
end ); end );
if TFile.Exists( dllFile ) then if TFile.Exists( dllFileLocal ) then
begin begin
success := True; successLocal := True;
TThread.Queue( nil, TThread.Queue( nil,
procedure procedure
begin begin
Memo.Lines.Add( Format( 'ERFOLG: DLL wurde erstellt: %s', [dllFile] ) ); Memo.Lines.Add( Format( 'ERFOLG: DLL wurde erstellt: %s', [dllFileLocal] ) );
end ); end );
end end
else else
begin begin
TThread.Queue( nil, TThread.Queue( nil,
procedure procedure
begin begin
Memo.Lines.Add( 'FEHLER: Linken fehlgeschlagen. .dll-Datei nicht erstellt.' ); Memo.Lines.Add( 'FEHLER: Linken fehlgeschlagen. .dll-Datei nicht erstellt.' );
end ); end );
end; end;
finally finally
if TFile.Exists( llFile ) then if TFile.Exists( llFileLocal ) then
TFile.Delete( llFile ); TFile.Delete( llFileLocal );
if TFile.Exists( objFile ) then if TFile.Exists( objFileLocal ) then
TFile.Delete( objFile ); TFile.Delete( objFileLocal );
if not success and TFile.Exists( dllFile ) then if not successLocal and TFile.Exists( dllFileLocal ) then
TFile.Delete( dllFile ); TFile.Delete( dllFileLocal );
end; end;
Memo.Lines.Add( '---------------------------------------------------------' ); TThread.Queue( nil,
Memo.Lines.Add( 'DLL wird aufgerufen:' ); procedure
begin
Memo.Lines.Add( '---------------------------------------------------------' );
Memo.Lines.Add( 'DLL wird aufgerufen:' );
if TFile.Exists( dllFile ) then if TFile.Exists( dllFileLocal ) then
begin begin
var var
Runner := TLLVMRunner.Create( dllFile ); Runner := TLLVMRunner.Create( dllFileLocal );
try try
Runner.Execute; Runner.Execute( InputInteger ); // <--- HIER DEN INPUT-PARAMETER ÜBERGEBEN
finally finally
Runner.Free; Runner.Free;
end; end;
end; end;
Memo.Lines.Add( 'DLL beendet.' );
Memo.CaretPosition := TCaretPosition.Create( Memo.Lines.Count-1, 1 );
end );
end ); end );
Memo.Lines.Add( 'DLL beendet.' );
end; end;
procedure TMainForm.WebBrowser1DidFinishLoad( ASender: TObject ); procedure TMainForm.WebBrowser1DidFinishLoad( ASender: TObject );
+110 -159
View File
@@ -3,6 +3,16 @@
let workspace; let workspace;
const isWebView = window.chrome && window.chrome.webview; 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) === // === LLVM-IR-GENERATOR (FINALE VERSION) ===
Blockly.LLVM = new Blockly.Generator('LLVM'); Blockly.LLVM = new Blockly.Generator('LLVM');
Blockly.LLVM.ORDER_ATOMIC = 0; Blockly.LLVM.ORDER_ATOMIC = 0;
@@ -27,50 +37,31 @@
"helpUrl": "" "helpUrl": ""
}, },
{ {
"type": "current_datetime_utc", "type": "host_get_integer_input",
"message0": "Aktueller UTC-Zeitstempel", "message0": "Hole Integer-Eingabe von Host",
"output": "DateTimeUTC", "output": "Number",
"colour": 120, "colour": 290,
"tooltip": "Liefert den aktuellen Zeitstempel in UTC.", "tooltip": "Liefert einen Integer-Wert, der vom Delphi-Host übergeben wurde.",
"helpUrl": "" "helpUrl": ""
}, },
{ {
"type": "get_datetime_component", "type": "host_add_integers",
"message0": "Hole %1 von Zeitstempel %2 in Zeitzone %3", "message0": "Addiere %1 und %2 auf Host",
"args0": [ "args0": [
{ {
"type": "field_dropdown", "type": "input_value",
"name": "COMPONENT", "name": "NUM1",
"options": [ "check": "Number"
["Jahr", "YEAR"],
["Monat", "MONTH"],
["Tag", "DAY"],
["Stunde", "HOUR"],
["Minute", "MINUTE"],
["Sekunde", "SECOND"]
]
}, },
{ {
"type": "input_value", "type": "input_value",
"name": "DATETIME", "name": "NUM2",
"check": "DateTimeUTC" "check": "Number"
},
{
"type": "field_dropdown",
"name": "TIMEZONE",
"options": [
["UTC", "UTC"],
["ET (New York)", "ET"],
["WET (London)", "WET"],
["CET (Frankfurt)", "CET"],
["AEST (Sydney)", "AEST"],
["JST (Tokyo)", "JST"]
]
} }
], ],
"output": "Number", "output": "Number",
"colour": 120, "colour": 290, // Host-Farbe
"tooltip": "Extrahiert eine Komponente (Jahr, Monat, etc.) aus einem UTC-Zeitstempel und konvertiert sie in die angegebene Zeitzone.", "tooltip": "Addiert zwei Integer-Werte unter Verwendung einer Host-Funktion.",
"helpUrl": "" "helpUrl": ""
} }
]); ]);
@@ -83,63 +74,58 @@
}; };
Blockly.LLVM.workspaceToCode = function(workspace) { Blockly.LLVM.workspaceToCode = function(workspace) {
Blockly.LLVM.regCounter = 1; Blockly.LLVM.regCounter = 1;
Blockly.LLVM.generatedStrings = new Set(); Blockly.LLVM.generatedStrings = new Set();
Blockly.LLVM.globalDeclarations = ''; Blockly.LLVM.globalDeclarations = '';
Blockly.LLVM.functionDeclarations = ''; Blockly.LLVM.functionDeclarations = '';
const allVariables = workspace.getAllVariables(); const allVariables = workspace.getAllVariables();
let varAllocations = ''; let varAllocations = '';
if (allVariables.length > 0) { if (allVariables.length > 0) {
allVariables.forEach(v => { allVariables.forEach(v => {
const safeVarName = v.name.replace(/ /g, '_'); const safeVarName = v.name.replace(/ /g, '_');
varAllocations += ` @${safeVarName} = common global i32 0\n`; varAllocations += ` @${safeVarName} = common global i32 0\n`;
}); });
} }
const topBlock = workspace.getTopBlocks(true)[0]; const topBlock = workspace.getTopBlocks(true)[0];
const code = topBlock ? Blockly.LLVM.blockToCode(topBlock) : ''; 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`; // NEU: Deklaration der Host-Funktion (Signatur, die der Host implementiert)
moduleCode += `%struct.DateTimeUTC = type { i32, i32, i32, i32, i32, i32 }\n\n`; 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) { if (Blockly.LLVM.functionDeclarations) {
moduleCode += '; Global string constants for timezones\n'; moduleCode += Blockly.LLVM.functionDeclarations + '\n';
moduleCode += Blockly.LLVM.globalDeclarations + '\n'; }
}
moduleCode += `; Host Callback Declarations\n`; moduleCode += `@gLogIntProc = common global void (i32)* null\n`;
moduleCode += `declare void @get_current_datetime_utc(%struct.DateTimeUTC*)\n`; // NEU: Globaler Zeiger für die Host-Additionsfunktion
moduleCode += `declare i32 @get_datetime_component_in_zone(%struct.DateTimeUTC*, i32, i8*)\n`; moduleCode += `@gHostAddIntegersProc = common global i32 (i32, i32)* null\n\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`;
if (varAllocations) { if (varAllocations) {
moduleCode += '; Global Variables\n'; moduleCode += '; Global Variables\n';
moduleCode += varAllocations + '\n'; moduleCode += varAllocations + '\n';
} }
moduleCode += 'define dllexport void @StarteLogik(void (i32)* %LogIntProc, void (%struct.DateTimeUTC*)* %GetCurrentDateTimeUTCProc, i32 (%struct.DateTimeUTC*, i32, i8*)* %GetComponentInZoneProc) {\n'; // StarteLogik-Signatur erweitern, um den neuen Funktionszeiger-Parameter zu akzeptieren
moduleCode += 'entry:\n'; moduleCode += 'define dllexport void @StarteLogik(void (i32)* %LogIntProc, i32 %hostInputInteger, i32 (i32, i32)* %HostAddIntegersProc) {\n';
moduleCode += ' store void (i32)* %LogIntProc, void (i32)** @gLogIntProc\n'; moduleCode += 'entry:\n';
moduleCode += ' store void (%struct.DateTimeUTC*)* %GetCurrentDateTimeUTCProc, void (%struct.DateTimeUTC*)** @gGetCurrentDateTimeUTCProc\n'; moduleCode += ' store void (i32)* %LogIntProc, void (i32)** @gLogIntProc\n';
moduleCode += ' store i32 (%struct.DateTimeUTC*, i32, i8*)* %GetComponentInZoneProc, i32 (%struct.DateTimeUTC*, i32, i8*)** @gGetComponentInZoneProc\n\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 += code.split('\n').map(line => line ? ' ' + line : '').join('\n');
moduleCode += '}\n'; moduleCode += '\n ret void\n';
return moduleCode; moduleCode += '}\n';
return moduleCode;
}; };
Blockly.LLVM.scrub_ = function(block, code) { Blockly.LLVM.scrub_ = function(block, code) {
const nextBlock = block.nextConnection && block.nextConnection.targetBlock(); const nextBlock = block.nextConnection && block.nextConnection.targetBlock();
const nextCode = nextBlock ? Blockly.LLVM.blockToCode(nextBlock) : ''; const nextCode = nextBlock ? Blockly.LLVM.blockToCode(nextBlock) : '';
@@ -180,7 +166,7 @@
let valueReg; let valueReg;
let code = ''; let code = '';
if (valueCode.startsWith('@')) { if (valueCode.startsWith('@')) {
valueReg = Blockly.LLVM.newReg(); valueReg = Blockly.LLVM.newReg();
code += `${valueReg} = load i32, i32* ${valueCode}\n`; code += `${valueReg} = load i32, i32* ${valueCode}\n`;
@@ -189,61 +175,33 @@
} else { } else {
valueReg = valueCode; valueReg = valueCode;
} }
const logPtrReg = Blockly.LLVM.newReg(); const logPtrReg = Blockly.LLVM.newReg();
code += `${logPtrReg} = load void (i32)*, void (i32)** @gLogIntProc\n`; code += `${logPtrReg} = load void (i32)*, void (i32)** @gLogIntProc\n`;
code += `call void ${logPtrReg}(i32 ${valueReg})\n`; code += `call void ${logPtrReg}(i32 ${valueReg})\n`;
return code; return code;
}; };
Blockly.LLVM.forBlock['current_datetime_utc'] = function(block) { Blockly.LLVM.forBlock['host_get_integer_input'] = function(block) {
const structPtrReg = Blockly.LLVM.newReg(); const inputParamReg = '%hostInputInteger';
const getUtcProcPtrReg = Blockly.LLVM.newReg(); return [inputParamReg, Blockly.LLVM.ORDER_ATOMIC];
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['get_datetime_component'] = function(block) { Blockly.LLVM.forBlock['host_add_integers'] = function(block) {
const datetimePtr = Blockly.LLVM.valueToCode(block, 'DATETIME', Blockly.LLVM.ORDER_ATOMIC) || 'null'; const num1 = Blockly.LLVM.valueToCode(block, 'NUM1', Blockly.LLVM.ORDER_ATOMIC) || '0';
const component = block.getFieldValue('COMPONENT'); const num2 = Blockly.LLVM.valueToCode(block, 'NUM2', Blockly.LLVM.ORDER_ATOMIC) || '0';
const timezone = block.getFieldValue('TIMEZONE');
const timezoneGlobalName = `@.str.timezone_${timezone.replace(/[^a-zA-Z0-9_]/g, '')}`; const funcPtrReg = Blockly.LLVM.newReg();
const timezoneStringLength = new TextEncoder().encode(timezone).length + 1; const resultReg = Blockly.LLVM.newReg();
if (!Blockly.LLVM.generatedStrings.has(timezoneGlobalName)) { let code = '';
Blockly.LLVM.globalDeclarations += `${timezoneGlobalName} = private unnamed_addr constant [${timezoneStringLength} x i8] c"${timezone}\\00", align 1\n`; // Lade den globalen Funktionszeiger, der vom Host gesetzt wird
Blockly.LLVM.generatedStrings.add(timezoneGlobalName); 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; return [code + resultReg, Blockly.LLVM.ORDER_ATOMIC];
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];
};
const toolboxXml = ` const toolboxXml = `
<xml> <xml>
@@ -256,12 +214,10 @@
</category> </category>
<category name="Host" colour="290"> <category name="Host" colour="290">
<block type="host_log_integer"></block> <block type="host_log_integer"></block>
<block type="host_get_integer_input"></block>
<block type="host_add_integers"></block>
</category> </category>
<category name="Datum &amp; Zeit" colour="120"> </xml>`; // <--- DAS FEHLENDE BACKTICK HIER IST DIE LÖSUNG!
<block type="current_datetime_utc"></block>
<block type="get_datetime_component"></block>
</category>
</xml>`;
workspace = Blockly.inject('blocklyDiv', { workspace = Blockly.inject('blocklyDiv', {
toolbox: toolboxXml, toolbox: toolboxXml,
@@ -310,20 +266,23 @@
console.log(code); console.log(code);
} }
} }
window.generateAndPostCode = generateAndPostCode; // Exponiere für Buttons window.generateAndPostCode = generateAndPostCode;
window.saveWorkspace = function() { window.saveWorkspace = function() {
// Diese Funktion kann den Arbeitsbereich manuell speichern und einen Alert anzeigen
const state = Blockly.serialization.workspaces.save(workspace); const state = Blockly.serialization.workspaces.save(workspace);
localStorage.setItem('blocklyWorkspace', JSON.stringify(state)); localStorage.setItem('blocklyWorkspace', JSON.stringify(state));
alert('Arbeitsbereich gespeichert!'); 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); } 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) { function handleCodeUpdate(event) {
if (event.isUiEvent) return; // Nur Änderungen durch Blöcke, nicht UI-Interaktionen if (event.isUiEvent) return;
generateAndPostCode(); // Dies triggert jetzt sowohl Speichern als auch Generieren/Posten debouncedGenerateAndPostCode();
} }
workspace.addChangeListener(handleCodeUpdate); workspace.addChangeListener(handleCodeUpdate);
loadWorkspace(); loadWorkspace();
@@ -331,34 +290,26 @@
// --- Bedingte UI-Elemente und Splitter-Logik --- // --- Bedingte UI-Elemente und Splitter-Logik ---
const outputContainer = document.getElementById('outputContainer'); const outputContainer = document.getElementById('outputContainer');
const blocklyDiv = document.getElementById('blocklyDiv'); const blocklyDiv = document.getElementById('blocklyDiv');
if (isWebView) { if (isWebView) {
// Wenn im WebView, outputContainer bleibt ausgeblendet (CSS default: display: none;) blocklyDiv.style.width = '100%';
// BlocklyDiv die gesamte Breite geben setTimeout(function() {
blocklyDiv.style.width = '100%'; Blockly.svgResize(workspace);
// Blockly-Workspace an neue Größe anpassen, mit einer kleinen Verzögerung für WebView }, 100);
setTimeout(function() {
Blockly.svgResize(workspace);
}, 100); // 100ms Verzögerung, kann bei Bedarf angepasst werden.
// 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'); const buttonBar = document.querySelector('.button-bar');
if (buttonBar) { if (buttonBar) {
const saveButton = buttonBar.querySelector('button[onclick="saveWorkspace()"]'); const saveButton = buttonBar.querySelector('button[onclick="saveWorkspace()"]');
const loadButton = buttonBar.querySelector('button[onclick="loadWorkspace()"]'); const loadButton = buttonBar.querySelector('button[onclick="loadWorkspace()"]');
if (saveButton) saveButton.remove(); if (saveButton) saveButton.remove();
if (loadButton) loadButton.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()"]'); const postButton = buttonBar.querySelector('button[onclick="generateAndPostCode()"]');
if (postButton) postButton.remove(); // Oder postButton.textContent = "Code senden"; if (postButton) postButton.remove();
} }
} else { } else {
// Wenn NICHT im WebView, outputContainer einblenden und Splitter-Logik initialisieren outputContainer.style.display = 'flex';
outputContainer.style.display = 'flex'; // Einblenden als Flex-Container
const outputArea = document.getElementById('outputArea'); const outputArea = document.getElementById('outputArea');
const splitter = document.getElementById('splitter'); const splitter = document.getElementById('splitter');
const container = document.getElementById('container'); const container = document.getElementById('container');
@@ -377,13 +328,13 @@
const containerRect = container.getBoundingClientRect(); const containerRect = container.getBoundingClientRect();
const splitterWidth = splitter.offsetWidth; const splitterWidth = splitter.offsetWidth;
const outputAreaMarginLeft = parseInt(window.getComputedStyle(outputArea).marginLeft); const outputAreaMarginLeft = parseInt(window.getComputedStyle(outputArea).marginLeft);
const totalContentWidth = containerRect.width - splitterWidth - outputAreaMarginLeft; const totalContentWidth = containerRect.width - splitterWidth - outputAreaMarginLeft;
let newBlocklyWidth = e.clientX - containerRect.left; let newBlocklyWidth = e.clientX - containerRect.left;
const minBlocklyWidth = parseFloat(window.getComputedStyle(blocklyDiv).minWidth); const minBlocklyWidth = parseFloat(window.getComputedStyle(blocklyDiv).minWidth);
const minOutputAreaWidth = parseFloat(window.getComputedStyle(outputArea).minWidth); const minOutputAreaWidth = parseFloat(window.getComputedStyle(outputArea).minWidth);
if (newBlocklyWidth < minBlocklyWidth) { if (newBlocklyWidth < minBlocklyWidth) {
newBlocklyWidth = minBlocklyWidth; newBlocklyWidth = minBlocklyWidth;
} }
@@ -400,7 +351,7 @@
blocklyDiv.style.width = `${newBlocklyWidth}px`; blocklyDiv.style.width = `${newBlocklyWidth}px`;
outputArea.style.width = `${newOutputAreaWidth}px`; outputArea.style.width = `${newOutputAreaWidth}px`;
Blockly.svgResize(workspace); Blockly.svgResize(workspace);
}); });
@@ -410,12 +361,12 @@
document.body.style.cursor = ''; document.body.style.cursor = '';
}); });
// Initiales Resize für Nicht-WebView: Setzt die initialen Breiten const initialOutputAreaWidth = 500;
// Basierend auf der Startbreite der outputArea (500px)
const initialOutputAreaWidth = 500; // Aus CSS initialer Wert
const totalFlexContentWidth = container.offsetWidth - splitter.offsetWidth - parseInt(window.getComputedStyle(outputArea).marginLeft); const totalFlexContentWidth = container.offsetWidth - splitter.offsetWidth - parseInt(window.getComputedStyle(outputArea).marginLeft);
let initialBlocklyWidth = totalFlexContentWidth - initialOutputAreaWidth; let initialBlocklyWidth = totalFlexContentWidth - initialOutputAreaWidth;
const minBlocklyWidth = parseFloat(window.getComputedStyle(blocklyDiv).minWidth);
if (initialBlocklyWidth < minBlocklyWidth) { if (initialBlocklyWidth < minBlocklyWidth) {
initialBlocklyWidth = minBlocklyWidth; initialBlocklyWidth = minBlocklyWidth;
outputArea.style.width = `${totalFlexContentWidth - initialBlocklyWidth}px`; outputArea.style.width = `${totalFlexContentWidth - initialBlocklyWidth}px`;
@@ -423,7 +374,7 @@
outputArea.style.width = `${initialOutputAreaWidth}px`; outputArea.style.width = `${initialOutputAreaWidth}px`;
} }
blocklyDiv.style.width = `${initialBlocklyWidth}px`; blocklyDiv.style.width = `${initialBlocklyWidth}px`;
Blockly.svgResize(workspace); Blockly.svgResize(workspace);
} }
}; };