i am building a Compiler and i get this Error Code, when i compile the programm:
compiler_main.pas(55,4) Error: Type mismatch
the code is the following:
unit Compiler_main;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, SynEdit, Forms, Controls, Graphics, Dialogs,
StdCtrls, ExtCtrls;
type
{ TForm1 }
TForm1 = class(TForm)
GroupBox1: TGroupBox;
GroupBox2: TGroupBox;
Image1: TImage;
Image2: TImage;
Splitter1: TSplitter;
Splitter2: TSplitter;
SynEdit1: TSynEdit;
SynEdit2: TSynEdit;
SynEdit3: TSynEdit;
procedure Image1Click(Sender: TObject);
procedure Image2Click(Sender: TObject);
procedure setForCompiling(var dataSect: array of String; var textSect: array of String; var bssSect: array of String);
procedure setDataSect (var dataSect: array of String);
//////////FUNCTIONS////////////////////
function compile () : boolean;
private
{ private declarations }
public
{ public declarations }
end;
var
Form1: TForm1;
var INTarray: array of integer;
implementation
{$R *.lfm}
{ TForm1 }
{
procedure Tform1.setTextSect (var textSect: array of String);
begin
end; }
procedure Tform1.setDataSect(var dataSect: array of String);
begin
setLength (dataSect, 1);
dataSect[0]:= 'section .data';
end;
procedure Tform1.setForCompiling(var dataSect: array of String; var textSect: array of String; var bssSect: array of String);
begin
Synedit2.Lines.Clear;
Synedit3.Lines.Clear;
setDataSect (dataSect);
end;
function getMistake (mistake: boolean) : boolean;
begin
if mistake = false then
begin
getMistake:= true;
end else
begin
getMistake:= false;
end;
end;
function Tform1.compile() : boolean;
var momLine : integer = 0;
var totalLines : integer;
var posBracketOpen : Byte;
var posBracketClose : Byte;
var posTextBracketOpen : Byte;
var posTextBracketClose: Byte;
var mistake : boolean = false;
var dataASM : array of string;
var textASM : array of string;
var bssASM : array of String;
begin
setForCompiling(dataASM, textASM, bssASM);
totalLines:= synEdit1.lines.count;
while momline < totalLines do
begin
inc (momline);
end;
compile:= getMistake (mistake);
end;
procedure TForm1.Image1Click(Sender: TObject);
begin
Showmessage ('Ein deutscher Compiler, programmiert von NONAME. NUR UNTER LINUX BENUTZBAR!');
end;
procedure TForm1.Image2Click(Sender: TObject);
begin
if Compile = true then
begin
Synedit3.Lines.add ('Fehlerfrei Compiliert!');
end else
begin
Synedit3.Lines.add ('Es wurde mindestens ein Fehler festgestellt! Bitte korrigiere diesen.');
end;
end;
end.
my Programm is programmed in Lazarus and bases on NASM and gets Compiled in the newest Laz-version in Linux.
I hope, you can help me.
Ok, it works now.
This is the setLength that generates error:
procedure Tform1.setDataSect(var dataSect: array of String);
begin
setLength (dataSect, 1);
dataSect[0]:= 'section .data';
end;
This code is incorrect because dataSect is not a dynamic array as you would expect but an open array parameter that you can't change.
You will need to change your function declaration to not use open array:
first use builtin type TStringDynArray (I am not sure if it available in Lazarus), or declare it like
type TStringDynArray = array of string;
Then use this type everywhere instead array of string.
procedure Tform1.setDataSect(dataSect: TStringDynArray);
begin
setLength (dataSect, 1);
dataSect[0]:= 'section .data';
end;
Also you don't need to use var parameter var dataSect: TStringDynArray - dynamic arrays are always passed by reference and all changes will be applied to original array, if you want to prevent this - you will need to explicitly create new copy of array like NewDataSect := Copy(dataSect);
Related
I'm new to programming and am trying to make a delphi tictactoe game using a 3x3 array, as a way to help me learn the basics.
I'm using a stringgrid which I wanted to show the state of the board, the user places an X on the cell (via mouse click). I was hoping at this part in the code it would add the 'X' to the gameboard array using the stringgrid.cell(ARow,ACol) for the position. I've now learnt that this cannot work as the array is a char type. Is there a way around this?
I know this is not the best way to make a game, but it's helping me learn. Any help would be very appreciated. Here's my code:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ButtonGroup,
Vcl.Grids;
type
TForm1 = class(TForm)
Button1: TButton;
label1: TLabel;
StringGrid1: TStringGrid;
Button2: TButton;
procedure StringGrid1SelectCell(Sender: TObject; ACol, ARow: Integer;
var CanSelect: Boolean);
procedure Button2Click(Sender: TObject);
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure restart();
procedure compTurn();
end;
var
Form1: TForm1;
gameBoard: array [1 .. 3, 1 .. 3] of char; // array for the game board.
iScore: Integer;
bTurn: Boolean = True; // O's go = false, X's go = true.
implementation
{$R *.dfm}
{ TForm1 }
procedure TForm1.Button2Click(Sender: TObject);
begin
restart;
end;
procedure TForm1.compTurn;
begin
// if (bTurn = false) then
label1.Caption := 'Computer`s turn';
end;
procedure TForm1.FormShow(Sender: TObject);
begin
restart;
end;
procedure TForm1.restart;
var
i, j: Integer;
begin
// place ? in the cells
StringGrid1.Enabled := True;
for i := 1 to 3 do
for j := 1 to 3 do
StringGrid1.cells[i, j] := '?';
label1.Caption := 'Player X`s turn';
end;
procedure TForm1.StringGrid1SelectCell(Sender: TObject; ACol, ARow: Integer;
var CanSelect: Boolean);
Var
sX: string;
pos: Integer;
begin
sX := 'X';
CanSelect := false;
// Place a X or O in the cell depending on whose turn it is
if (bTurn = True) then
StringGrid1.cells[ACol, ARow] := sX // and gameBoard[ACol, ARow]
else
bTurn := false; // computer's turn to move
compTurn; // make sure user can't add an X
StringGrid1.Enabled := false;
end;
end.```
I was hoping at this part in the code it would add the 'X' to the
gameboard array using the stringgrid.cell(ARow,ACol) for the position.
I've now learnt that this cannot work as the array is a char type. Is
there a way around this?
The problem you are facing here is that in your StringGrid1SelectCell event handler you defined your sX variable as string. So if you try to assign value of sX to your array of char Delphi won't let you since you cant store entire string into a sigle char.
To achieve desired functionality you should change your sX variable into a Char type instead. This will then allow you to write its value into your array.
Now you might be thinking that this would lead to problems since you also need to assign value of your sX variable to specific String Grid Cell since String Grid Cells are of String type. You won't.
You can always assign a single char to a string. This will change the length of that string to one an it will contain only the specific character you provided.
So in short:
Assigning String to Char does not work
Assigning Char to string does work
PS Also don't forget that if you need to execute multiple commands when if clause condition is met you need to enclose those commands with begin..end block.
So your code should look something like this (haven't tested it)
procedure TForm1.StringGrid1SelectCell(Sender: TObject; ACol, ARow: Integer;
var CanSelect: Boolean);
Var
sX: Char; //Change type from String to Char
pos: Integer;
begin
sX := 'X';
CanSelect := false;
// Place a X or O in the cell depending on whose turn it is
if (bTurn = True) then
//Enclose multiple commands with begin..end block
begin
StringGrid1.cells[ACol, ARow] := sX // and gameBoard[ACol, ARow]
GameBoard[ACol,ARow] := sX;
else
bTurn := false; // computer's turn to move
compTurn; // make sure user can't add an X
StringGrid1.Enabled := false;
end;
I need to create procedure in delphi which will cast array of anonymous type to array of specified type. What I'am trying to accomplish:
procedure Foo (myArray: array of AnonymousType; typeName: string)
begin
//do smth on this array
end;
This is actually a procedure for a game where player can define his own script. This script is called at runtime so above structure is the only one which will actually work.
What I'am trying to do is pass 2 parameters into this function:
array of user defined type (which I don't know in game)
type of this array passed by string
And then procedure should convert passed array into this specific type.
I read about TList, but I cannot declare static type.
In .NET I'am able to pass array of dynamic (or even object) and cast it to destination type using linq or simply iterate through dynamic list.
Is something like that actually possible in delphi?
Any help will be appreciated.
Delphi DX10
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
System.Generics.Collections, System.Generics.Defaults, System.TypInfo;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
function Foo<T>(AItem: T): String;
end;
type
TA1 = record
X1, X2: Integer;
end;
TA2 = record
W1, W2: String;
end;
TA3 = record
Z1, Z2: real;
end;
var
Form1: TForm1;
A1: TA1;
A2: TA2;
A3: TA3;
implementation
{$R *.dfm}
function TForm1.Foo<T>(AItem: T): String;
var
LTypeInfo: PTypeInfo;
GUID: TGUID;
begin
{ Get type info }
LTypeInfo := TypeInfo(T);
{ Now you know name of type }
Result := LTypeInfo.Name;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Foo<TA1>(A1);
Foo<TA2>(A2);
Foo<TA3>(A3);
end;
end.
I'm trying to create an array to store every item of a TStringList into an array which size may vary depending on the quantity of items in said TStringList.
I know my syntax is wrong and what I want is probably a dynamic array, so the [0..100] is probably wrong aswell but I could not find any alternative syntax online.
ProductAvailabilityResult = Class(TRemotable)
private
FResultArray : array[1..100] of string;
published
property ResultArray[Index: Integer]: array of string read FResultArray write FResultArray;
End;
And this is how I would invoke it and populate it. conditionList being my TStringList which I would populate into my array.
for I := 0 to conditionList.Count - 1 do
begin
aProductAvailabilityResult.ResultArray[I] := conditionList[I];
end;
In case you may or may not have alternative suggestions to what i'm doing, the reason for this setup is because it's a web service app sending results over a SOAP server, and I don't think my PHP/Soap client can read TStringLists, so I need to pass it to an array first.
Let me know, Thanks!
Your syntax for declaring an array property is close, but you need to use getter/setter methods instead of direct field access, and array properties cannot be declared as published:
type
ProductAvailabilityResult = class(TRemotable)
private
FResultArray : array of string;
function GetResultArray(Index: Integer): string;
function GetResultArrayCount: Integer;
procedure SetResultArray(Index: Integer; const Value: string);
procedure SetResultArrayCount(Value: Integer);
public
property ResultArray[Index: Integer]: string read GetResultArray write SetResultArray default;
property ResultArrayCount: Integer read GetResultArrayCount write SetResultArrayCount;
end;
function ProductAvailabilityResult.GetResultArray(Index: Integer): string;
begin
Result := FResultArray[Index];
end;
function ProductAvailabilityResult.GetResultArrayCount: Integer;
begin
Result := Length(FResultArray);
end;
procedure ProductAvailabilityResult.SetResultArray(Index: Integer; const Value: string);
begin
FResultArray[Index] := Value;
end;
procedure ProductAvailabilityResult.SetResultArrayCount(Value: Integer);
begin
SetLength(FResultArray, Value);
end;
Then you can do this:
aProductAvailabilityResult.ResultArrayCount := conditionList.Count;
for I := 0 to conditionList.Count - 1 do
begin
aProductAvailabilityResult[I] := conditionList[I];
end;
You might want to consider adding a method to copy strings from a source TStrings:
type
ProductAvailabilityResult = class(TRemotable)
private
...
public
procedure AssignStrings(AStrings: TStrings);
...
end;
procedure ProductAvailabilityResult.AssignStrings(AStrings: TStrings);
var
I: Integer;
begin
SetLength(FResultArray, AStrings.Count);
for I := 0 to AStrings.Count - 1 do
FResultArray[I] := AStrings[I];
end;
aProductAvailabilityResult.AssignStrings(conditionList);
You've declared an array property, albeit with some syntax errors. However, you state in the question and comments that you want a property that is a dynamic array. That's different from an array property.
Declare a dynamic array property like so:
type
ProductAvailabilityResult = class(TRemotable)
private
FResultArray: TArray<string>;
published
property ResultArray: TArray<string> read FResultArray write FResultArray;
end;
Populate it like this:
var
i: Integer;
List: TStringList;
par: ProductAvailabilityResult;
arr: TArray<string>;
....
List := ...;
par := ...;
SetLength(arr, List.Count);
for i := 0 to List.Count-1 do
arr[i] := List[i];
par.ResultArray := arr;
I'm aware that in Delphi, when you want to allow the use of the index operator, [], you must do something like,
property Item[index: integer]: integer read GetData; default;
How would one go about implementing a multidimensional array in Delphi such that it allows the use of something like:
matrix := TMatrix<integer>.Create(3,3);
matrix[0][2] := 5;
WriteLn(matrix[0][2]);
You can't use [][] like that. But you can declare multiple indexes in a single property instead, eg:
type
TMatrix<T> = class
private
function GetData(index1, index2: Integer): T;
procedure SetData(index1, index2: Integer; value: T);
public
constructor Create(dim1, dim2: Integer);
property Item[index1, index2: Integer]: T read GetData write SetData; default;
end;
Then you can do this:
matrix := TMatrix<integer>.Create(3,3);
matrix[0, 2] := 5;
WriteLn(matrix[0, 2]);
If you wish, you can use [][] to access elements. If your type is an array (two-dimensional or jagged dynamic) then this method of accessing the elements is baked into the language. For a user-defined type then you need to implement it.
There is no way to implement [][] in a single step in a user-defined type. What you need to do is break the process into two separate parts. The first part is to implement [] to return a row of your matrix. Then implement [] on that row to return an element. Here is an example:
type
TMatrix<T> = class
public
type
TRow = record
private
FMatrix: TMatrix<T>;
FRowIndex: Integer;
function GetItem(ColIndex: Integer): T; inline;
procedure SetItem(ColIndex: Integer; const Value: T); inline;
public
property Items[ColIndex: Integer]: T read GetItem write SetItem; default;
end;
private
FData: TArray<TArray<T>>;
function GetRow(RowIndex: Integer): TRow; inline;
public
constructor Create(RowCount, ColCount: Integer);
property Rows[RowIndex: Integer]: TRow read GetRow; default;
end;
{ TMatrix<T>.TRow }
function TMatrix<T>.TRow.GetItem(ColIndex: Integer): T;
begin
Result := FMatrix.FData[FRowIndex, ColIndex];
end;
procedure TMatrix<T>.TRow.SetItem(ColIndex: Integer; const Value: T);
begin
FMatrix.FData[FRowIndex, ColIndex] := Value;
end;
{ TMatrix<T> }
constructor TMatrix<T>.Create(RowCount, ColCount: Integer);
begin
inherited Create;
SetLength(FData, RowCount, ColCount);
end;
function TMatrix<T>.GetRow(RowIndex: Integer): TRow;
begin
Result.FMatrix := Self;
Result.FRowIndex := RowIndex;
end;
However, having shown that this is possible, I would suggest that is more idiomatic to use an array property with two indices. That would mean that you would access the matrix with M[Row,Col] rather than M[Row][Col]. This particular idiom (M[Row,Col]) is not found in all languages so you may be unfamiliar with it. Supporting that might look like this:
type
TMatrix<T> = class
public
type
TRow = record
private
FMatrix: TMatrix<T>;
FRowIndex: Integer;
function GetItem(ColIndex: Integer): T; inline;
procedure SetItem(ColIndex: Integer; const Value: T); inline;
public
property Items[ColIndex: Integer]: T read GetItem write SetItem; default;
end;
private
FData: TArray<TArray<T>>;
function GetRow(RowIndex: Integer): TRow; inline;
function GetItem(RowIndex, ColIndex: Integer): T; inline;
procedure SetItem(RowIndex, ColIndex: Integer; const Value: T); inline;
public
constructor Create(RowCount, ColCount: Integer);
property Rows[RowIndex: Integer]: TRow read GetRow;
property Items[RowIndex, ColIndex: Integer]: T read GetItem write SetItem; default;
end;
{ TMatrix<T>.TRow }
function TMatrix<T>.TRow.GetItem(ColIndex: Integer): T;
begin
Result := FMatrix.FData[FRowIndex, ColIndex];
end;
procedure TMatrix<T>.TRow.SetItem(ColIndex: Integer; const Value: T);
begin
FMatrix.FData[FRowIndex, ColIndex] := Value;
end;
{ TMatrix<T> }
constructor TMatrix<T>.Create(RowCount, ColCount: Integer);
begin
inherited Create;
SetLength(FData, RowCount, ColCount);
end;
function TMatrix<T>.GetRow(RowIndex: Integer): TRow;
begin
Result.FMatrix := Self;
Result.FRowIndex := RowIndex;
end;
function TMatrix<T>.GetItem(RowIndex, ColIndex: Integer): T;
begin
Result := FData[RowIndex, ColIndex];
end;
procedure TMatrix<T>.SetItem(RowIndex, ColIndex: Integer; const Value: T);
begin
FData[RowIndex, ColIndex] := Value;
end;
Note that in this version we have elected to make Items be the default property. Which means that if you want to access a row you would have to name the Rows property explicitly: M.Rows[RowIndex].
I have a non-windowed component with a date property. I would like to make this component data aware with read and write capabilities to a date field. (In other words, if I change the date at runtime, I would like to write the new date property value to the dataset.) I have googled for examples, but I haven't been able to find any. Found several read-only examples, e.g., TDbLabel, but none that allow changes to be written to the dataset. If anyone can point me to an example, I would be grateful.
Here is a generic code approach to your question
unit Unit1;
interface
uses
Classes
,DB
,DBCtrls
;
type
TDataAwareComponent = class(TComponent)
private
{ private declarations }
FDataLink : TFieldDataLink;
FFromDateChange : Boolean;
function GetDataField: string;
function GetDataSource: TDataSource;
function GetField: TField;
function GetReadOnly: Boolean;
procedure SetDataField(const Value: string);
procedure SetDataSource(const Value: TDataSource);
procedure SetReadOnly(const Value: Boolean);
function GetDateProperty: TDateTime;
procedure SetDateProperty(const Value: TDateTime);
protected
{ protected declarations }
procedure DataChange(Sender : TObject);
public
{ public declarations }
constructor Create(AOwner : TComponent);override;
destructor Destroy;override;
public
property Field : TField read GetField;
property DateProperty : TDateTime read GetDateProperty write SetDateProperty;
published
{ published declarations }
property DataSource : TDataSource read GetDataSource write SetDataSource;
property DataField : string read GetDataField write SetDataField;
property ReadOnly : Boolean read GetReadOnly write SetReadOnly;
end;
implementation
{ TDataAwareComponent }
{------------------------------------------------------------------------------}
constructor TDataAwareComponent.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FDataLink := TFieldDataLink.Create;
FDataLink.Control := Self;
FDataLink.OnDataChange := DataChange;
end;
{------------------------------------------------------------------------------}
destructor TDataAwareComponent.Destroy;
begin
FDataLink.Free;
FDataLink := nil;
inherited;
end;
{------------------------------------------------------------------------------}
procedure TDataAwareComponent.DataChange(Sender: TObject);
begin
// Here a visual object would display the underlying field value.
// For example if this component was a TEdit
// the code would be something like
{ if FDataLink.Active then
Self.Text := Field.AsString;
And on the exit event of the TEdit the code would be reversed
so the field can take the value the user entered.
Since you do not need any UI interaction propably this event
is useless to you.
}
// Of course there are more issues you should cover to
// have a complete working solution.
// This is only demostration code.
end;
{------------------------------------------------------------------------------}
function TDataAwareComponent.GetDataField: string;
begin
Result := FDataLink.FieldName
end;
{------------------------------------------------------------------------------}
function TDataAwareComponent.GetDataSource: TDataSource;
begin
Result := FDataLink.DataSource;
end;
{------------------------------------------------------------------------------}
function TDataAwareComponent.GetDateProperty: TDateTime;
begin
//You do not need a separate property since you can access directly the field value.
Result := 0;
if Assigned(Field) and (Field.DataType in [ftTime,ftDate,ftDateTime] then
Result := Field.AsDateTime;
end;
{------------------------------------------------------------------------------}
function TDataAwareComponent.GetField: TField;
begin
Result : FDataLink.Field;
end;
{------------------------------------------------------------------------------}
function TDataAwareComponent.GetReadOnly: Boolean;
begin
Result := FDataLink.ReadOnly;
end;
{------------------------------------------------------------------------------}
procedure TDataAwareComponent.SetDataField(const Value: string);
begin
FDataLink.FieldName := Value;
end;
{------------------------------------------------------------------------------}
procedure TDataAwareComponent.SetDataSource(const Value: TDataSource);
begin
FDataLink.DataSource := Value;
end;
{------------------------------------------------------------------------------}
procedure TDataAwareComponent.SetDateProperty(const Value: TDateTime);
begin
if Assigned(Field) then
begin
FFromDateChange := True;
try
Field.DataSet.Edit;
Field.Value := Value;
finally
FFromDateChange := False;
end;
end;
end;
{------------------------------------------------------------------------------}
procedure TDataAwareComponent.SetReadOnly(const Value: Boolean);
begin
FDataLink.ReadOnly := Value;
end;
{------------------------------------------------------------------------------}
end.
It'd be a bit of code, but if your component has events triggered when its value changes, then that could be used to send updated values to the dataset, if it's in the dsEdit state. You'd also need to handle the various TDataSource Events to know when the dataset is changing.