On this page ...

Customization

From the five parts of a language definition, Freon generates a complete work environment containing an editor, scope provider (scoper), type provider (typer), validator, standard library, and utilities such as a parser and deparser. These individual components are collectively referred to as fre-tools. Other sections of this documentation describe how to fine-tune the generated fre-tools using the files in the defs folder (i.e. the folder containing your definition files). (See for instance Defining_an_editor.) In this section, we focus on deeper customizations implemented through TypeScript code.

Stacked Architecture

Customization is made possible by Freon’s stacked architecture. This architecture makes it possible to create your language, and customize it step-by-step in an agile fashion. Freon’s design philosophy can be summarized by the following quote from the well-known graphical user interface designer Alan Kay:

Simple things should be simple, complex things should be possible. (Alan Kay, Turing Award Winner)

(Read our Background to understand why we have chosen this architecture.) Freon supports three levels of customization.

  1. The default level, which is always available.
  2. The definition level, where the fre-tools are being generated based on the input from the definition files (e.g. a .scope file).
  3. The customization level, where handwritten TypeScript code can be added or used to replace parts of the generated output.

Freon aims to provide around 80% of the required functionality of the work environment through the first two levels.

Image 'documentation/layered-architecture2.png' seems to be missing
Figure 1. The Stacked Architecture

Freon combines the definitions for each tool, together with the customized TypeScript code into one application, where the third level precedes the second, and the second level precedes the first. For example, the generated editor determines for each concept in the language which projection to use, in the following order:

  1. use the hand-written projection from the Customization level, when this is present. If not, the editor will
  2. use the projection generated from the .edit definition from the Freon definition Level, when this definition is present. Finally, when no definition is present, the editor will
  3. fall back to the default projection, the one generated when no .edit definition file is present.
Image 'documentation/fall-through.png' seems to be missing
Figure 2. Projection Lookup for an AST Node

This layered approach allows language engineers to start quickly with a working environment and refine it gradually at either the definition or customization level. For this purpose, the Command Line Interface includes separate commands for generating the different fre-tools.

Customizability of the Fre-Tools

This table gives an overview of the fre-tools, and shows how you can adjust them to your needs.

Workbench ComponentHas DefaultLevel 2 DefinitionLevel 3 Definition
language structureno.astno
projectional editoryes.edityes
scope provideryes.scopeyes
validatoryes.validyes
type provideryes.typeyes
interpreternonone available yetyes
standard libraryyes.astnot yet
parseryes.edityes (using AGL)
unparseryes.editnot yet
json exporter/importeryes.astno
visitor pattern implementationyes.astcan be extended
web appyesnone availablecan be changed or replaced

If you are missing a specific tool or feature, please contact us at info@openmodeling.nl.

Adding A Fre-tool

Freon automatically generates TypeScript templates to support your customizations. You can find these in the files ~/freon/editor/CustomYourLanguageNameProjection.ts, ~/freon/editor/CustomYourLanguageNameActions.ts, etc. (where YourLanguageName is a placeholder for the name of the language as defined in your .ast file). You can use these files to add your own special elements.

The custom files can be renamed and/or place them in another location, or you can create your own set of custom projection files. In that case, you must update the configuration file ~/freon/config/FreonConfiguration.

The following is an example of a generated configuration file that registers custom projections, actions, and other extensions.

// Insurance/src/freon/config/FreonConfiguration.ts

// Generated by the Freon Language Generator.
// TEMPLATE: ConfigurationTemplate.generate(...)

import { type FreProjection, type FreCombinedActions, type FreTyper, type FreStdlib, type FreScoper } from "@freon4dsl/core";

import { CustomInsuranceModelActions, CustomInsuranceModelProjection } from "../editor/index.js";
import { CustomInsuranceModelScoper } from "../scoper/index.js";
import { CustomInsuranceModelTyperPart } from "../typer/CustomInsuranceModelTyperPart.js";
import { CustomInsuranceModelValidator } from "../validator/index.js";
import { CustomInsuranceModelStdlib } from "../stdlib/CustomInsuranceModelStdlib.js";
import { type InsuranceModelCheckerInterface } from "../validator/gen/index.js";

/**
 * Class FreonConfiguration is the place where you can add all your customisations.
 * These will be used through the 'freonConfiguration' constant by any generated
 * part of your language environment.
 */
class FreonConfiguration {
    // add your custom editor projections here
    customProjection: FreProjection[] = [new CustomInsuranceModelProjection()];
    // add your custom editor actions here
    customActions: FreCombinedActions[] = [new CustomInsuranceModelActions()];
    // add your custom validations here
    customValidations: InsuranceModelCheckerInterface[] = [new CustomInsuranceModelValidator()];
    // add your custom scopers here
    customScopers: FreScoper[] = [new CustomInsuranceModelScoper()];
    // add your custom type-providers here
    customTypers: FreTyper[] = [new CustomInsuranceModelTyperPart()];
    // add extra predefined instances here
    customStdLibs: FreStdlib[] = [new CustomInsuranceModelStdlib()];
}

export const freonConfiguration = new FreonConfiguration();

Replacing a Fre-tool

All fre-tools can be fully replaced at the third level, overriding both the default and definition levels. This is done by implementing their respective interfaces.

For the parser and unparser, replacement is currently the only supported form of customization. More information on these interfaces can be found in the following documentation sections:

© 2018 - 2025 Freon contributors - Freon is open source under the MIT License.