A robust TOML (Tom's Obvious, Minimal Language) parser and serializer for Free Pascal, almost fully compliant with the TOML v1.0.0 specification.
Note
Our extensive test suite (53 tests) ensures that TOML-FP adheres to the TOML v1.0.0 specification, covering all essential data types, structures, and edge cases.
- TOML Parser for Free Pascal
TOML-FP provides a complete solution for working with TOML configuration files in Free Pascal applications. Whether you need to read configuration files or generate them, TOML-FP offers an intuitive API with comprehensive type support and robust error handling.
- Full TOML v1.0.0 Compliance: Supports all TOML data types and structures
- Type-Safe API: Strong typing with compile-time checks
- Memory Management: Automatic cleanup with proper object lifecycle management
- Error Handling: Detailed error messages and exception handling
- Serialization: Convert Pascal objects to TOML and back
- Documentation: Comprehensive examples and API documentation
- Test Suite: Comprehensive test suite (53 items)
- More tests for complex structures: Nested tables, array of tables, and dotted keys.
- More tests for serializing TOML to file: Making sure objects save back into the correct TOML format.
- More tests for error handling: Robust testing of invalid inputs and ensuring appropriate error messages or handling mechanisms.
- Free Pascal Compiler 3.2.2 or later
- Lazarus IDE 3.6 (for running tests)
- Free Pascal Compiler 3.2.2 or later
- Lazarus IDE 3.6 (for running tests)
-
Clone the repository:
git clone https://github.com/yourusername/toml-fp.git
-
Add to your project:
- Copy the
src
directory to your project - Add units to your project's search path
- Add to your uses clause:
uses TOML, TOML.Types, TOML.Parser, TOML.Serializer;
- Copy the
program BasicParseTOML;
{$mode objfpc}{$H+}{$J-}
uses
TOML;
var
Config: TTOMLTable;
RevisionValue, ProjectValue, ProjectName: TTOMLValue;
ProjectTable: TTOMLTable;
begin
// Parse TOML from file
Config := ParseTOMLFromFile('config.toml');
try
// Access a string value
if (Config.TryGetValue('revision', RevisionValue)) then
WriteLn('The value of ''revision'' is ', RevisionValue.AsString);
// Access nested values safely
if Config.TryGetValue('project', ProjectValue) and
(ProjectValue is TTOMLTable) then
begin
ProjectTable := TTOMLTable(ProjectValue);
if ProjectTable.TryGetValue('name', ProjectName) then
WriteLn('Project Name: ', ProjectName.AsString)
else
WriteLn('Project name not found');
end
else
WriteLn('Project configuration not found');
finally
Config.Free;
end;
end.
Example config.toml
:
# config.toml
revision = "1.2.1af"
[project]
name = "My Amazing Project"
version = "1.0.0"
program BasicSerializeTOML;
{$mode objfpc}{$H+}{$J-}
uses
TOML;
var
Config: TTOMLTable;
Database: TTOMLTable;
begin
Config := TOMLTable;
try
Database := TOMLTable;
Database.Add('host', TOMLString('localhost'));
Database.Add('port', TOMLInteger(5432));
Config.Add('database', Database);
if SerializeTOMLToFile(Config, 'config.toml') then
WriteLn('Configuration saved successfully')
else
WriteLn('Error saving configuration');
finally
Config.Free;
end;
end.
var
Config: TTOMLTable;
Tags: TTOMLArray;
begin
Config := TOMLTable;
try
Tags := TOMLArray;
Tags.Add(TOMLString('pascal'));
Tags.Add(TOMLString('toml'));
Config.Add('tags', Tags);
WriteLn(SerializeTOML(Config));
finally
Config.Free;
end;
end.
var
Config: TTOMLTable;
Database: TTOMLTable;
begin
Config := TOMLTable;
try
Database := TOMLTable;
Database.Add('host', TOMLString('localhost'));
Database.Add('port', TOMLInteger(5432));
Config.Add('database', Database);
WriteLn(SerializeTOML(Config));
finally
Config.Free;
end;
end.
program BasicSerializeTOML;
{$mode objfpc}{$H+}{$J-}
uses
TOML, SysUtils;
var
Config, ServerConfig: TTOMLTable;
Ports: TTOMLArray;
SerializedTOML: string;
begin
Config := TOMLTable;
try
// Create a nested table
ServerConfig := TOMLTable;
ServerConfig.Add('host', TOMLString('127.0.0.1'));
ServerConfig.Add('enabled', TOMLBoolean(True));
// Create and populate an array
Ports := TOMLArray;
Ports.Add(TOMLInteger(80));
Ports.Add(TOMLInteger(443));
ServerConfig.Add('ports', Ports);
// Add the server config to main config
Config.Add('server', ServerConfig);
// Add some basic metadata
Config.Add('version', TOMLFloat(1.0));
Config.Add('last_updated', TOMLDateTime(Now));
// Serialize to TOML format
SerializedTOML := SerializeTOML(Config);
WriteLn('Generated TOML:');
WriteLn(SerializedTOML);
// Save to file
if SerializeTOMLToFile(Config, 'config.toml') then
WriteLn('Successfully saved to file')
else
WriteLn('Error saving to file');
finally
Config.Free;
end;
end.
This will generate TOML like:
version = 1.0
last_updated = 2024-03-20T15:30:45Z
[server]
host = "127.0.0.1"
enabled = true
ports = [ 80, 443 ]
Note: All values are properly type-checked and memory-managed. The library ensures that:
- Each value has the correct TOML type
- Arrays maintain type consistency
- All objects are properly freed
- Type conversions are validated
Note
Only free the top-level owner table to avoid memory management issues.
- Proper memory management is essential to prevent memory leaks in your applications. TOML-FP follows a clear ownership hierarchy where parent tables own their nested tables and values. Here's how it works:
- When you create a
TTOMLTable
, it acts as an owner for any nested tables or values you add to it.
- Use the
Add
method to insert nested tables or values into a parent table. - Example:
var
Config: TTOMLTable;
Database: TTOMLTable;
begin
Config := TOMLTable;
Database := TOMLTable;
Config.Add('database', Database);
// ...
Config.Free; // This frees all nested tables and values
end.
- Only free the top-level owner table.
- Frees all nested tables and values automatically.
- Example:
var
Config: TTOMLTable;
begin
Config := TOMLTable;
// ... add nested tables and values ...
Config.Free; // This frees all nested tables and values
end.
- Do not manually free nested tables or values to prevent memory management issues.
TTOMLValue
- Base type for all TOML valuesTTOMLTable
- Represents a TOML tableTTOMLArray
- Represents a TOML arrayTTOMLString
- Represents a TOML string valueTTOMLInteger
- Represents a TOML integer valueTTOMLFloat
- Represents a TOML float valueTTOMLBoolean
- Represents a TOML boolean valueTTOMLDateTime
- Represents a TOML datetime value
TOMLString
function TOMLString(const AValue: string): TTOMLString;
Creates a TOML string value.
TOMLInteger
function TOMLInteger(const AValue: Int64): TTOMLInteger;
Creates a TOML integer value.
TOMLFloat
function TOMLFloat(const AValue: Double): TTOMLFloat;
Creates a TOML float value.
TOMLBoolean
function TOMLBoolean(const AValue: Boolean): TTOMLBoolean;
Creates a TOML boolean value.
TOMLDateTime
function TOMLDateTime(const AValue: TDateTime): TTOMLDateTime;
Creates a TOML datetime value.
TOMLArray
function TOMLArray: TTOMLArray;
Creates a TOML array.
TOMLTable
function TOMLTable: TTOMLTable;
Creates a TOML table.
ParseTOML
function ParseTOML(const ATOML: string): TTOMLTable;
Parses a TOML-formatted string into a TTOMLTable
object.
function ParseTOML(const ATOML: string): TTOMLTable;
begin
Result := TOML.Parser.ParseTOMLString(ATOML);
end;
ParseTOMLFromFile
function ParseTOMLFromFile(const AFileName: string): TTOMLTable;
Parses a TOML file into a TTOMLTable
object.
function ParseTOMLFromFile(const AFileName: string): TTOMLTable;
var
FileStream: TFileStream;
StringStream: TStringStream;
begin
FileStream := TFileStream.Create(AFileName, fmOpenRead or fmShareDenyWrite);
try
StringStream := TStringStream.Create('');
try
StringStream.CopyFrom(FileStream, 0);
Result := ParseTOMLString(StringStream.DataString);
finally
StringStream.Free;
end;
finally
FileStream.Free;
end;
end;
SerializeTOML
function SerializeTOML(const AValue: TTOMLValue): string;
Serializes a TTOMLValue
into a TOML-formatted string.
function SerializeTOML(const AValue: TTOMLValue): string;
begin
Result := TOML.Serializer.SerializeTOML(AValue);
end;
SerializeTOMLToFile
function SerializeTOMLToFile(const AValue: TTOMLValue; const AFileName: string): Boolean;
Serializes a TTOMLValue
and saves it to a file.
function SerializeTOMLToFile(const AValue: TTOMLValue; const AFileName: string): Boolean;
begin
Result := TOML.Serializer.SerializeTOMLToFile(AValue, AFileName);
end;
The library includes a comprehensive test suite (53 items).
To run the tests:
- Open
tests/TestRunner.lpi
in Lazarus - Build and run the project
See Test-Coverage-Overview.md for details.
- Basic Types:
- Strings, Integers, Floats, Booleans, DateTime
- Advanced Structures:
- Multiline and Literal Strings
- Homogeneous and Mixed-Type Arrays
- Nested and Inline Tables
- Serialization:
- Accurate conversion of Pascal objects to TOML format and vice versa
- Error Handling:
- Robust tests for invalid inputs, duplicate keys, and other edge cases
$ ./TestRunner.exe -a --format=plain
Time:00.006 N:53 E:0 F:0 I:0
TTOMLTestCase Time:00.006 N:53 E:0 F:0 I:0
Test01_StringValue
Test02_IntegerValue
...
Test70_ComplexKeys
Number of run tests: 53
Number of errors: 0
Number of failures: 0
Heap dump by heaptrc unit of path\to\TestRunner.exe
2991 memory blocks allocated : 195551/208600
2991 memory blocks freed : 195551/208600
0 unfreed memory blocks : 0
True heap size : 294912 (256 used in System startup)
True free heap : 294656
Check out the examples
directory and the test cases, tests/TestCaseTOML.pas
, for more detailed usage examples:
- Basic Configuration Reading
- Writing Complex Data Structures
- Error Handling Patterns
- Integration Examples
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature
) - Commit your changes (
git commit -m 'Add some AmazingFeature'
) - Push to the branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
Please ensure:
- Code follows the project's style guidelines
- All tests pass
- New features include appropriate tests
- Documentation is updated
This project is licensed under the MIT License - see the LICENSE.md file for details.
- TOML specification creators and maintainers
- Free Pascal and Lazarus communities
- All contributors to this project