From 3cfca1f167237bd3b075e742ddd4263f2c6a0952 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Wed, 11 Jun 2025 01:33:52 +0200 Subject: [PATCH] Blockly review --- Blockly/Delphi/LLVM.Runner.pas | 72 ++++++--- Blockly/Delphi/MainUnit.fmx | 15 +- Blockly/Delphi/MainUnit.pas | 134 +++++++++------- Blockly/script.js | 269 ++++++++++++++------------------- 4 files changed, 248 insertions(+), 242 deletions(-) diff --git a/Blockly/Delphi/LLVM.Runner.pas b/Blockly/Delphi/LLVM.Runner.pas index 923780e..774f86f 100644 --- a/Blockly/Delphi/LLVM.Runner.pas +++ b/Blockly/Delphi/LLVM.Runner.pas @@ -9,24 +9,34 @@ 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 - private - FModule: HMODULE; - FStarteLogik: TStarteLogikProc; - class var - FLog: TStrings; - class procedure LogInteger( AValue: Integer ); static; stdcall; - public - constructor Create(const ADLLPath: string); - destructor Destroy; override; - procedure Execute; - class property Log: TStrings read FLog write FLog; - end; - + TLLVMRunner = class + private + FModule: HMODULE; + FStarteLogik: TStarteLogikProc; + 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( AInputInteger: Integer ); + class property Log: TStrings read FLog write FLog; + end; + implementation { TLLVMRunner } @@ -50,19 +60,33 @@ begin inherited; end; -procedure TLLVMRunner.Execute; -begin - if Assigned( FStarteLogik ) then - // Führe die DLL-Funktion aus und übergebe unsere Callback-Methode - FStarteLogik( LogInteger ); +// NEUE Implementierung der Host-Funktion +class function TLLVMRunner.HostAddIntegers( ANum1: Integer; ANum2: Integer ): Integer; +begin + 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; + +// 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 aufruft. -// Sie muss "static" (class procedure) sein, um eine feste Adresse zu haben. +// 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. diff --git a/Blockly/Delphi/MainUnit.fmx b/Blockly/Delphi/MainUnit.fmx index bc7a112..055e1b6 100644 --- a/Blockly/Delphi/MainUnit.fmx +++ b/Blockly/Delphi/MainUnit.fmx @@ -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 diff --git a/Blockly/Delphi/MainUnit.pas b/Blockly/Delphi/MainUnit.pas index 73ff336..f0e8cc3 100644 --- a/Blockly/Delphi/MainUnit.pas +++ b/Blockly/Delphi/MainUnit.pas @@ -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 ); diff --git a/Blockly/script.js b/Blockly/script.js index a85c642..5c7ae3e 100644 --- a/Blockly/script.js +++ b/Blockly/script.js @@ -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) : ''; @@ -180,7 +166,7 @@ let valueReg; let code = ''; - + if (valueCode.startsWith('@')) { valueReg = Blockly.LLVM.newReg(); code += `${valueReg} = load i32, i32* ${valueCode}\n`; @@ -189,61 +175,33 @@ } else { valueReg = valueCode; } - + const logPtrReg = Blockly.LLVM.newReg(); code += `${logPtrReg} = load void (i32)*, void (i32)** @gLogIntProc\n`; code += `call void ${logPtrReg}(i32 ${valueReg})\n`; 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 = ` @@ -256,12 +214,10 @@ + + - - - - - `; + `; // <--- 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(); @@ -331,34 +290,26 @@ // --- Bedingte UI-Elemente und Splitter-Logik --- 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 - 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. - - // Entferne die Schaltflächen "Speichern" und "Laden", wenn im WebView - // da die Speicherung automatisch erfolgt und der Host den Code direkt bekommt. + + if (isWebView) { + blocklyDiv.style.width = '100%'; + setTimeout(function() { + Blockly.svgResize(workspace); + }, 100); + 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'); const container = document.getElementById('container'); @@ -377,13 +328,13 @@ const containerRect = container.getBoundingClientRect(); const splitterWidth = splitter.offsetWidth; const outputAreaMarginLeft = parseInt(window.getComputedStyle(outputArea).marginLeft); - + const totalContentWidth = containerRect.width - splitterWidth - outputAreaMarginLeft; let newBlocklyWidth = e.clientX - containerRect.left; const minBlocklyWidth = parseFloat(window.getComputedStyle(blocklyDiv).minWidth); const minOutputAreaWidth = parseFloat(window.getComputedStyle(outputArea).minWidth); - + if (newBlocklyWidth < minBlocklyWidth) { newBlocklyWidth = minBlocklyWidth; } @@ -400,7 +351,7 @@ blocklyDiv.style.width = `${newBlocklyWidth}px`; outputArea.style.width = `${newOutputAreaWidth}px`; - + Blockly.svgResize(workspace); }); @@ -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`; @@ -423,7 +374,7 @@ outputArea.style.width = `${initialOutputAreaWidth}px`; } blocklyDiv.style.width = `${initialBlocklyWidth}px`; - + Blockly.svgResize(workspace); } };