-
Notifications
You must be signed in to change notification settings - Fork 3
Clarion Language Reference
- Clarion source files MUST use ANSI/ASCII characters only
- Unicode is strictly NOT allowed
- All source code, comments, and string literals must be ANSI/ASCII
- This includes:
- Variable names
- Procedure names
- Comments
- String constants
- All program text
- Clarion is case-insensitive for keywords, labels, and identifiers
-
IF,if, andIfare all equivalent - Variable names, procedure names, labels are case-insensitive
-
MyVariable,myvariable, andMYVARIABLErefer to the same variable - String literals ARE case-sensitive:
'Hello'≠'hello'
-
Square brackets
[]are used for array/string indexing and slicing -
Syntax:
Variable[index]orVariable[start : end] - Arrays are 1-based (first element is index 1)
- Examples:
MyArray[5] ! Access element 5
MyString[1 : 10] ! Substring from position 1 to 10
MyQueue[Index].Field ! Access field in queue element
MyArray[i + 1] ! Expression as index- Every compilable Clarion source file (compiled directly as part of a project) must begin with either
PROGRAMorMEMBERas the first statement - PROGRAM or MEMBER must be on line 1 (the very first line of the file)
- No comments or any other content is allowed before PROGRAM/MEMBER
- Best practice: Indent PROGRAM/MEMBER (not at column 0) - even the Clarion IDE formatter moves them away from column 0
- Required for compilation in a
.cwprojfile (Clarion uses MSBuild)
- Include files (
.inc,.equ, or other extensions) do NOT requirePROGRAMorMEMBERstatements - Include files are inserted into other source files using the
INCLUDEstatement - Include files can contain:
- Data declarations
- Equates (constants)
- Procedure prototypes
- MAP structures
- Any valid Clarion code that can be inserted at the include point
- Syntax:
INCLUDE('filename')orINCLUDE('filename'),ONCE - The
,ONCEattribute prevents duplicate inclusion if the file is included multiple times - The compiler makes multiple passes over the source files, so depending on where an INCLUDE sits it can be processed more than once — always use
,ONCEon header-style includes to avoid duplicate-declaration errors - Class header (
.inc) files conventionally carry their own OMIT-based include guard as well — see the Include-Guard Idiom under OMIT and COMPILE - Include files are processed at compile time, inserting their contents at the point of inclusion
- Example:
PROGRAM
INCLUDE('EQUATES.EQU'),ONCE
INCLUDE('PROTOTYPES.INC'),ONCE
GlobalVar LONG
CODE
! Program code! File: CONSTANTS.EQU
! This file does NOT need PROGRAM or MEMBER
MaxRecords EQUATE(1000)
AppTitle EQUATE('My Application')! File: MYPROCS.INC
! This file does NOT need PROGRAM or MEMBER
MAP
MODULE('KERNEL32')
GetTickCount PROCEDURE(),ULONG
END
END- Indicates the source file contains the main program entry point
- Syntax:
PROGRAM - Only one PROGRAM file per application
- Required as the first statement in any source file that is not a PROGRAM file
- Used for source modules containing procedures called by a PROGRAM
- Syntax:
MEMBERorMEMBER('program')
MEMBER[(program)]
[MAP
[MODULE('library')
ExternalProc PROCEDURE()
END]
LocalProc PROCEDURE()
END]
[local data declarations]
[procedures]-
program (optional): String constant containing the filename (without extension) of the PROGRAM source file
- If omitted, creates a "universal member module" that can be compiled in any program by adding it to the project
- If omitted, you must have a MAP structure that prototypes the procedures it contains
-
Local MAP: May contain a local MAP structure with procedure declarations
- Procedures declared in this MAP are available only to other procedures in the same MEMBER module
- If source code for a procedure is in a separate file, the prototype must be in a MODULE structure within the MAP
-
Local Data: Data declared after MEMBER and before the first PROCEDURE statement is Member Local data
- Can only be accessed by procedures within the module (unless passed as a parameter)
- Memory allocation is Static
-
Procedures: Source code for the procedures in the MEMBER module
! Universal member module
MEMBER
MAP
LocalHelper PROCEDURE(LONG pValue)
END
SharedData LONG,STATIC
LocalHelper PROCEDURE(LONG pValue)
CODE
RETURN pValue * 2
MainProc PROCEDURE()
CODE
SharedData = LocalHelper(10)
RETURN! Member bound to specific program
MEMBER('MyApp')
MAP
MODULE('KERNEL32')
GetTickCount PROCEDURE(),ULONG
END
UtilityProc PROCEDURE()
END
UtilityProc PROCEDURE()
CODE
MESSAGE('Tick count: ' & GetTickCount())
RETURN- All labels must start at column 0 (first column)
- Labels include:
- Variable definitions
- Procedure declarations
- Procedure implementations
- Member implementations/definitions
- Data structure declarations (GROUP, QUEUE, FILE, etc.)
- MAP, END, and dot (.) terminators must NOT be at column 0
- These keywords/terminators must be indented (at least one space)
- Best practice: Follow the Clarion IDE formatter conventions for indentation
- Examples:
MyProc PROCEDURE()
LocalVar LONG
CODE
IF x > 0 THEN
y = 1
. ! Dot terminator - indented, not at column 0
RETURN
MyData GROUP
Field1 LONG
END ! END terminator - indented, not at column 0| Must be at Column 0 | Must be Indented (NOT column 0) |
|---|---|
| Variable labels | MAP |
| Procedure names | END |
| GROUP/QUEUE/FILE labels | Dot (.) terminators |
| ROUTINE labels | Module-level: PROGRAM, MEMBER (best practice) |
- In PROCEDURE/MEMBER implementations, all data definitions must appear before the CODE statement
- Procedures and methods never use a DATA keyword - everything before CODE is considered a data definition
- The CODE keyword marks the beginning of executable statements
- Example:
MyProc PROCEDURE()
! Everything here is data definition (no DATA keyword needed)
LocalVar LONG
TempString STRING(100)
CODE
! Executable statements go here
LocalVar = 10
RETURNThere are four general areas where data can be declared in a Clarion program:
- Location: In the PROGRAM module, after the keyword PROGRAM and before the CODE statement
- Visibility: Visible to every PROCEDURE in the PROGRAM
- Scope: Always in scope
- Memory Allocation: Static memory
- Availability: Available to every PROCEDURE in the PROGRAM
PROGRAM
GlobalCounter LONG
GlobalName STRING(100)
CODE
! Program code- Location: In a MEMBER module, after the keyword MEMBER and before the first PROCEDURE statement
- Visibility: Visible only to the set of PROCEDUREs contained in the MEMBER module
- Scope: Comes into scope when any PROCEDURE in the MODULE is called
- Memory Allocation: Static memory
- Parameters: May be passed as a parameter to PROCEDUREs in other MEMBER modules, if required
MEMBER('MyApp')
ModuleCounter LONG
ModuleData STRING(50)
MyProc PROCEDURE()
CODE
ModuleCounter += 1- Location: In a PROCEDURE, after the keyword PROCEDURE and before the CODE statement
- Important: Procedures do NOT use a DATA keyword; everything before CODE is considered a data definition
- Visibility: Visible only within the PROCEDURE in which it is declared, or any Local Derived Methods declared within the PROCEDURE
- Scope: Comes into scope when the PROCEDURE is called; goes out of scope when a RETURN statement executes
-
Memory Allocation: Dynamic memory
- Stack allocation for variables smaller than the stack threshold (5K default)
- Heap allocation for variables larger than the threshold
- Can be overridden with the STATIC attribute to make values persistent between calls
- FILE declarations are always allocated static memory (on the heap), even in Local Data sections
- Parameters: May be passed as a parameter to any other PROCEDURE
- Recursion: Allows true recursion, receiving a new copy of local variables each time called
MyProc PROCEDURE(LONG pParam)
! Everything here is data definition (no DATA keyword)
LocalVar LONG
LocalStr STRING(100)
StaticVar LONG,STATIC ! Persists between calls
LocalFile FILE,DRIVER('DOS') ! Always static/heap allocation
CODE
! Executable statements start here
LocalVar = pParam
RETURN- Location: In a ROUTINE, after the keyword DATA and before the CODE statement
- Visibility: Visible only within the ROUTINE in which it is declared
- Scope: Comes into scope when the ROUTINE is called; goes out of scope when an EXIT statement executes
-
Memory Allocation: Dynamic memory
- Stack allocation for variables smaller than the stack threshold (5K default)
- Heap allocation for variables larger than the threshold
- Parameters: May be passed as a parameter to any PROCEDURE
- Name Scope: ROUTINE has its own name scope; labels may duplicate variable names used in other ROUTINEs or the containing procedure
- Restrictions: Variables declared in a ROUTINE may NOT have the STATIC or THREAD attributes
RoutineLabel ROUTINE
[DATA
[local data declarations]
CODE]
[statements]- Location: Must be at the end of the CODE section of the PROCEDURE to which it belongs
-
Calling: Called using
DO RoutineLabel -
Termination: A ROUTINE is terminated by:
- End of source module
- Another ROUTINE or PROCEDURE declaration
- Implicit EXIT at the end of the routine implementation (EXIT keyword not required)
- Explicit EXIT statement (only needed to exit early from the routine)
-
DATA/CODE Keywords:
- If the ROUTINE has a DATA statement as the first entry after the label, then CODE is required before executable statements
- If the ROUTINE does NOT have a DATA statement, CODE is not required and all statements are compiled as code
- Clarion#: DATA and CODE keywords are required
- Visibility: All variables visible to the containing PROCEDURE are available in the ROUTINE (Procedure Local, Module Local, and Global data)
-
Implicit variables (
Name#, `Name# Clarion Language Reference
- Clarion source files MUST use ANSI/ASCII characters only
- Unicode is strictly NOT allowed
- All source code, comments, and string literals must be ANSI/ASCII
- This includes:
- Variable names
- Procedure names
- Comments
- String constants
- All program text
- Clarion is case-insensitive for keywords, labels, and identifiers
-
IF,if, andIfare all equivalent - Variable names, procedure names, labels are case-insensitive
-
MyVariable,myvariable, andMYVARIABLErefer to the same variable - String literals ARE case-sensitive:
'Hello'≠'hello'
-
Square brackets
[]are used for array/string indexing and slicing -
Syntax:
Variable[index]orVariable[start : end] - Arrays are 1-based (first element is index 1)
- Examples:
MyArray[5] ! Access element 5
MyString[1 : 10] ! Substring from position 1 to 10
MyQueue[Index].Field ! Access field in queue element
MyArray[i + 1] ! Expression as index- Every compilable Clarion source file (compiled directly as part of a project) must begin with either
PROGRAMorMEMBERas the first statement - PROGRAM or MEMBER must be on line 1 (the very first line of the file)
- No comments or any other content is allowed before PROGRAM/MEMBER
- Best practice: Indent PROGRAM/MEMBER (not at column 0) - even the Clarion IDE formatter moves them away from column 0
- Required for compilation in a
.cwprojfile (Clarion uses MSBuild)
- Include files (
.inc,.equ, or other extensions) do NOT requirePROGRAMorMEMBERstatements - Include files are inserted into other source files using the
INCLUDEstatement - Include files can contain:
- Data declarations
- Equates (constants)
- Procedure prototypes
- MAP structures
- Any valid Clarion code that can be inserted at the include point
- Syntax:
INCLUDE('filename')orINCLUDE('filename'),ONCE - The
,ONCEattribute prevents duplicate inclusion if the file is included multiple times - Include files are processed at compile time, inserting their contents at the point of inclusion
- Example:
PROGRAM
INCLUDE('EQUATES.EQU'),ONCE
INCLUDE('PROTOTYPES.INC'),ONCE
GlobalVar LONG
CODE
! Program code! File: CONSTANTS.EQU
! This file does NOT need PROGRAM or MEMBER
MaxRecords EQUATE(1000)
AppTitle EQUATE('My Application')! File: MYPROCS.INC
! This file does NOT need PROGRAM or MEMBER
MAP
MODULE('KERNEL32')
GetTickCount PROCEDURE(),ULONG
END
END- Indicates the source file contains the main program entry point
- Syntax:
PROGRAM - Only one PROGRAM file per application
- Required as the first statement in any source file that is not a PROGRAM file
- Used for source modules containing procedures called by a PROGRAM
- Syntax:
MEMBERorMEMBER('program')
MEMBER[(program)]
[MAP
[MODULE('library')
ExternalProc PROCEDURE()
END]
LocalProc PROCEDURE()
END]
[local data declarations]
[procedures]-
program (optional): String constant containing the filename (without extension) of the PROGRAM source file
- If omitted, creates a "universal member module" that can be compiled in any program by adding it to the project
- If omitted, you must have a MAP structure that prototypes the procedures it contains
-
Local MAP: May contain a local MAP structure with procedure declarations
- Procedures declared in this MAP are available only to other procedures in the same MEMBER module
- If source code for a procedure is in a separate file, the prototype must be in a MODULE structure within the MAP
-
Local Data: Data declared after MEMBER and before the first PROCEDURE statement is Member Local data
- Can only be accessed by procedures within the module (unless passed as a parameter)
- Memory allocation is Static
-
Procedures: Source code for the procedures in the MEMBER module
! Universal member module
MEMBER
MAP
LocalHelper PROCEDURE(LONG pValue)
END
SharedData LONG,STATIC
LocalHelper PROCEDURE(LONG pValue)
CODE
RETURN pValue * 2
MainProc PROCEDURE()
CODE
SharedData = LocalHelper(10)
RETURN! Member bound to specific program
MEMBER('MyApp')
MAP
MODULE('KERNEL32')
GetTickCount PROCEDURE(),ULONG
END
UtilityProc PROCEDURE()
END
UtilityProc PROCEDURE()
CODE
MESSAGE('Tick count: ' & GetTickCount())
RETURN- All labels must start at column 0 (first column)
- Labels include:
- Variable definitions
- Procedure declarations
- Procedure implementations
- Member implementations/definitions
- Data structure declarations (GROUP, QUEUE, FILE, etc.)
- MAP, END, and dot (.) terminators must NOT be at column 0
- These keywords/terminators must be indented (at least one space)
- Best practice: Follow the Clarion IDE formatter conventions for indentation
- Examples:
MyProc PROCEDURE()
LocalVar LONG
CODE
IF x > 0 THEN
y = 1
. ! Dot terminator - indented, not at column 0
RETURN
MyData GROUP
Field1 LONG
END ! END terminator - indented, not at column 0| Must be at Column 0 | Must be Indented (NOT column 0) |
|---|---|
| Variable labels | MAP |
| Procedure names | END |
| GROUP/QUEUE/FILE labels | Dot (.) terminators |
| ROUTINE labels | Module-level: PROGRAM, MEMBER (best practice) |
- In PROCEDURE/MEMBER implementations, all data definitions must appear before the CODE statement
- Procedures and methods never use a DATA keyword - everything before CODE is considered a data definition
- The CODE keyword marks the beginning of executable statements
- Example:
MyProc PROCEDURE()
! Everything here is data definition (no DATA keyword needed)
LocalVar LONG
TempString STRING(100)
CODE
! Executable statements go here
LocalVar = 10
RETURNThere are four general areas where data can be declared in a Clarion program:
- Location: In the PROGRAM module, after the keyword PROGRAM and before the CODE statement
- Visibility: Visible to every PROCEDURE in the PROGRAM
- Scope: Always in scope
- Memory Allocation: Static memory
- Availability: Available to every PROCEDURE in the PROGRAM
PROGRAM
GlobalCounter LONG
GlobalName STRING(100)
CODE
! Program code- Location: In a MEMBER module, after the keyword MEMBER and before the first PROCEDURE statement
- Visibility: Visible only to the set of PROCEDUREs contained in the MEMBER module
- Scope: Comes into scope when any PROCEDURE in the MODULE is called
- Memory Allocation: Static memory
- Parameters: May be passed as a parameter to PROCEDUREs in other MEMBER modules, if required
MEMBER('MyApp')
ModuleCounter LONG
ModuleData STRING(50)
MyProc PROCEDURE()
CODE
ModuleCounter += 1- Location: In a PROCEDURE, after the keyword PROCEDURE and before the CODE statement
- Important: Procedures do NOT use a DATA keyword; everything before CODE is considered a data definition
- Visibility: Visible only within the PROCEDURE in which it is declared, or any Local Derived Methods declared within the PROCEDURE
- Scope: Comes into scope when the PROCEDURE is called; goes out of scope when a RETURN statement executes
-
Memory Allocation: Dynamic memory
- Stack allocation for variables smaller than the stack threshold (5K default)
- Heap allocation for variables larger than the threshold
- Can be overridden with the STATIC attribute to make values persistent between calls
- FILE declarations are always allocated static memory (on the heap), even in Local Data sections
- Parameters: May be passed as a parameter to any other PROCEDURE
- Recursion: Allows true recursion, receiving a new copy of local variables each time called
MyProc PROCEDURE(LONG pParam)
! Everything here is data definition (no DATA keyword)
LocalVar LONG
LocalStr STRING(100)
StaticVar LONG,STATIC ! Persists between calls
LocalFile FILE,DRIVER('DOS') ! Always static/heap allocation
CODE
! Executable statements start here
LocalVar = pParam
RETURN- Location: In a ROUTINE, after the keyword DATA and before the CODE statement
- Visibility: Visible only within the ROUTINE in which it is declared
- Scope: Comes into scope when the ROUTINE is called; goes out of scope when an EXIT statement executes
-
Memory Allocation: Dynamic memory
- Stack allocation for variables smaller than the stack threshold (5K default)
- Heap allocation for variables larger than the threshold
- Parameters: May be passed as a parameter to any PROCEDURE
- Name Scope: ROUTINE has its own name scope; labels may duplicate variable names used in other ROUTINEs or the containing procedure
- Restrictions: Variables declared in a ROUTINE may NOT have the STATIC or THREAD attributes
RoutineLabel ROUTINE
[DATA
[local data declarations]
CODE]
[statements]- Location: Must be at the end of the CODE section of the PROCEDURE to which it belongs
-
Calling: Called using
DO RoutineLabel -
Termination: A ROUTINE is terminated by:
- End of source module
- Another ROUTINE or PROCEDURE declaration
- Implicit EXIT at the end of the routine implementation (EXIT keyword not required)
- Explicit EXIT statement (only needed to exit early from the routine)
-
DATA/CODE Keywords:
- If the ROUTINE has a DATA statement as the first entry after the label, then CODE is required before executable statements
- If the ROUTINE does NOT have a DATA statement, CODE is not required and all statements are compiled as code
-
Clarion#: DATA and CODE keywords are required
,
Name"): variables declared explicitly in a ROUTINE's DATA section are visible in that ROUTINE only, but the scope of implicit variables used in routine code depends on thelocal_implicitsproject define — when TRUE they are local to the ROUTINE; when FALSE (the default) they share the owning procedure's scope and are common to all its routines. (Per compiler engineer Alexey Solovjev — ClarionHub explanation.)
MyProc PROCEDURE()
ProcVar LONG
CODE
ProcVar = 10
DO MyRoutine
DO SimpleRoutine
RETURN
! ROUTINE with local data (requires DATA and CODE)
MyRoutine ROUTINE
DATA
RoutineVar LONG ! Separate scope from ProcVar
ProcVar STRING(20) ! Legal: duplicates procedure's ProcVar name
CODE
RoutineVar = 5
ProcVar = 'Text' ! This is the STRING, not the LONG
! Implicit EXIT here (no EXIT keyword needed)
! ROUTINE without local data (no DATA/CODE needed)
SimpleRoutine ROUTINE
MESSAGE('Simple routine')
ProcVar += 1 ! Access procedure's ProcVar
! Implicit EXIT here (no EXIT keyword needed)
! ROUTINE with early exit
CheckRoutine ROUTINE
IF ProcVar = 0
EXIT ! Early exit if condition met
.
MESSAGE('ProcVar is not zero')
! Implicit EXIT here-
DOandEXITstatements are very efficient - Accessing procedure-level local data is less efficient than accessing module-level or global data
- Implicit variables used only within the ROUTINE are less efficient than using local variables
- Each
RETURNstatement within a ROUTINE incurs a 40-byte overhead
| Scope | Location | Visibility | Memory | Lifetime |
|---|---|---|---|---|
| Global | PROGRAM module | All procedures | Static | Entire program |
| Module | MEMBER module | Procedures in same MEMBER | Static | First call to any procedure in module |
| Local | PROCEDURE | Within procedure only | Dynamic* | During procedure execution |
| Routine Local | ROUTINE | Within routine only | Dynamic | During routine execution |
*Local data: Stack (< 5K default) or Heap (≥ 5K); FILE always heap; STATIC attribute makes persistent
- When
?appears as a single character in column 0 (column one), the following statement only executes in DEBUG mode -
?is also used to begin field equate labels in other contexts - Example:
MyProc PROCEDURE()
CODE
x = 10
? Message('Debug mode: x = ' & x) ! Only executes in DEBUG mode
RETURN- A dot
.is used to end structures instead of theENDkeyword - The dot can appear on the same line as the structure or on a separate line by itself
- Any structure that normally requires
ENDcan use a dot instead - Examples:
! IF statement with dot on same line
IF A = B THEN C = D.
! IF statement with END on same line (space separates, no semicolon needed)
IF A = B THEN Message('Equal') END
! GROUP with dot on same line
MyGroup GROUP(DerivedGroupType).
! GROUP with dot on separate line
MyGroup GROUP
Field1 LONG
Field2 STRING(20)
.
! LOOP with dot
LOOP
x += 1
IF x > 10 THEN BREAK.
.- A semicolon
;separates multiple statements on the same line - Allows writing compact code with multiple statements per line
- Required when multiple statements appear on the same line (space alone is not sufficient)
- Exception:
END— or a period., which is exactly equivalent toEND— can follow a statement with just a space separator - Examples:
! Multiple statements require semicolons
a = 1; b = 2; c = 3
! END or dot (.) can follow with just a space (no semicolon needed)
IF A = B THEN Message('Equal') END
IF A = B THEN Message('Equal') . ! identical - . is END- By default, line breaks act as statement terminators
- Multiple statements can appear on separate lines without explicit terminators
- Example:
x = 5
y = 10
z = 15- Procedures are declared with the
PROCEDUREkeyword - Procedures do NOT have END statements
- A procedure is implicitly terminated when:
- Another procedure begins
- End of file is reached
ProcedureName PROCEDURE([parameters])[,attributes]
[local data declarations]
CODE
[statements]
[ROUTINE definitions]
! Next procedure or ROUTINE or EOF implicitly ends this procedureProcedure Attributes: Procedures can have multiple attributes specified after the parameter list, separated by commas:
- Return type: Data type keyword (BYTE, SHORT, LONG, STRING, etc.)
- NAME('exported_name'): Specifies exported DLL function name
- PROC: Indicates procedure attribute
- Other attributes: PRIVATE, EXTERNAL, VIRTUAL, etc.
Attribute Order: The return type can appear anywhere in the attribute list:
! All of these are valid:
MyProc PROCEDURE(),LONG,NAME('MyProc') ! Return type first
MyProc PROCEDURE(),NAME('MyProc'),LONG ! Return type last
MyProc PROCEDURE(),PROC,LONG,NAME('MyProc') ! Return type middleIMPORTANT:
- PROCEDURE implementations do NOT have END statements
-
A PROCEDURE ends when:
- Another PROCEDURE implementation starts
- A ROUTINE implementation starts
- End of file (EOF) is reached
-
RETURN statement:
- Only REQUIRED if PROCEDURE has a return type (e.g.,
,LONG,,STRING) - If no return type specified, RETURN is optional (implicit return at end)
- If return type specified, RETURN must provide a value
- Only REQUIRED if PROCEDURE has a return type (e.g.,
TestProc PROCEDURE()
a LONG
b LONG
CODE
a = 1
b = 2
RETURN
AnotherProc PROCEDURE()
CODE
! Previous procedure implicitly ended- The dot
.is an alternative to theENDkeyword for closing structures - The dot terminates ONLY the immediately enclosing structure
- Can appear inline with the last statement:
IF a = 1 THEN b = 2. - Or on its own line (must be indented, NOT at column 0)
- Only the dot character itself is the terminator - trailing whitespace and comments are separate
- Works for any structure that accepts END: IF, LOOP, GROUP, etc.
Examples:
! Inline dot terminator
IF x > 10 THEN y = 5.
! Dot on its own line
IF a = 1
b = 2
c = 3
.
! Nested structures with mixed END and dot
IF condition1
LOOP
IF condition2 THEN BREAK.
END
.The following structures require an END statement (or . terminator) to close them:
- The LOOP structure can be terminated in three ways:
-
ENDor.keyword -
WHILE condition- post-fix loop condition (evaluates at end of loop) -
UNTIL condition- post-fix loop condition (evaluates at end of loop)
-
- Inside a LOOP (or ACCEPT),
BREAKimmediately leaves the loop andCYCLEjumps back to the top for the next iteration - Both accept an optional label of an enclosing labelled LOOP —
BREAK label/CYCLE label— to control an outer loop from inside a nested one
! Standard LOOP with END
LOOP
[statements]
END
! LOOP with WHILE terminator
LOOP
[statements]
WHILE condition
! LOOP with UNTIL terminator
LOOP
[statements]
UNTIL condition
! FOR-style LOOP with counter
LOOP x = 1 TO 10
[statements]
END
! FOR-style with WHILE/UNTIL
LOOP x = 1 TO 10 BY 2
[statements]
WHILE conditionImportant: WHILE and UNTIL are loop terminators when they appear at the LOOP level, not conditions inside the loop body.
- Only the
IFstatement requiresENDor.to close the entire structure -
ELSIFandELSEdo NOT require their ownENDor. - The single
ENDor.closes the entire IF/ELSIF/ELSE structure -
THEN keyword: Optional keyword after the condition
- Provides syntactic clarity but does NOT affect structure
- A statement MUST follow THEN (can be on same line or next line)
- Common patterns:
-
IF a = 1 THEN RETURN.- statement on same line with dot terminator -
IF a = 1 THEN b = 2.- inline assignment with dot terminator -
IF a = 1 THEN b = 2 END- inline with END (space separates) -
IF a = 1 THENfollowed by statements on next lines
-
IF condition
[statements]
ELSIF condition
[statements]
ELSE
[statements]
END
! Or with dot terminator
IF condition THEN [statement]
[more statements]
.
! Inline forms
IF a = 1 THEN RETURN.
IF x > 10 THEN y = 5.
IF valid THEN process() ENDCASE expression
OF value1
[statements]
OF value2
[statements]
ELSE
[statements]
ENDEXECUTE index
[statements]
[statements]
[statements]
ENDBEGIN
[statements]
ENDAll the structures below are terminated by END or a period . — the two are interchangeable.
A structure type must be declared before it is referenced: a structure that embeds another via LIKE(SomeType) (or uses a ,TYPEd GROUP/QUEUE) requires that type to appear earlier in the source.
GroupName GROUP
Field1 LONG
Field2 STRING(20)
ENDQueueName QUEUE
Field1 LONG
Field2 STRING(20)
ENDFileName FILE,DRIVER('TOPSPEED')
Record RECORD
Field1 LONG
Field2 STRING(20)
END
ENDRecordName RECORD
Field1 LONG
Field2 STRING(20)
ENDClassName CLASS
Property1 LONG
Method1 PROCEDURE()
ENDInterfaceName INTERFACE
Method1 PROCEDURE()
ENDModuleName MODULE('filename')
Procedure1 PROCEDURE()
END MAP
MODULE('library')
ExternalProc PROCEDURE()
END
END- Use
!for single-line comments - Everything after
!on that line is a comment
! This is a comment
a = 1 ! This is also a comment- Clarion has no block-comment syntax — every comment line starts with
! - Multi-line documentation is written as consecutive
!lines:
! PROCEDURE: MyProc
! PURPOSE: Does something important
! PARAMETERS: none
MyProc PROCEDURE()-
|at the end of a line continues the statement on the next line — commonly used to keep long declarations and argument lists readable -
&|continues a string concatenation across lines:
Msg STRING('first part of the text ' &|
'second part of the text')-
.clw- Clarion source files -
.inc- Clarion include files -
.equ- Clarion equate files (constants) -
.int- interface definition files (INTERFACE declarations referenced by class headers, e.g. ABC'sABAPI.INT)
-
BYTE- 1-byte unsigned integer (0-255) -
SHORT- 2-byte signed integer -
USHORT- 2-byte unsigned integer -
LONG- 4-byte signed integer -
ULONG- 4-byte unsigned integer -
REAL- 4-byte floating point -
SREAL- 8-byte floating point -
DECIMAL- Packed decimal -
PDECIMAL- Packed decimal -
STRING- Fixed-length string -
CSTRING- Null-terminated string -
PSTRING- Pascal-style string (length prefix)
-
IF,ELSIF,ELSE -
LOOP,WHILE,UNTIL -
CASE,OF EXECUTE-
BREAK- Exit loop -
CYCLE- Continue to next iteration -
RETURN- Exit procedure -
EXIT- Exit application
-
GROUP- Group of related fields -
QUEUE- Dynamic array/table -
FILE- File declaration -
RECORD- Record structure -
CLASS- Object-oriented class -
INTERFACE- Interface definition -
MODULE- External module reference -
MAP- External procedure mapping
-
LOCAL- Local variable/procedure -
GLOBAL- Global variable/procedure -
EXTERNAL- External declaration -
STATIC- Static variable (retains value between calls) -
THREAD- Thread-local variable
-
PROCEDURE- Standard procedure -
FUNCTION- Procedure that returns a value (synonym for PROCEDURE) -
ROUTINE- Internal labeled section within a procedure
- MAP structures contain prototypes which declare PROCEDUREs and external source modules
- Used in PROGRAM, MEMBER module, or PROCEDURE (but not for members of a CLASS structure)
- Declares which procedures are available for use
Multiple MAP structures are legal in the same source file — the compiler merges them all, and the same MODULE may appear in several MAPs (declarations accumulate). Useful for grouping prototypes by API:
MAP
MODULE('TheSame.lib')
APIName1 PROCEDURE()
END
END
MAP
MODULE('TheSame.lib')
APIName2 PROCEDURE()
END
END- Declared in the PROGRAM source module
- Prototypes are available throughout the entire program
- Example:
PROGRAM
MAP
LoadIt PROCEDURE
SaveIt PROCEDURE(STRING pFilename)
END- Declared in a MEMBER module
- Prototypes are explicitly available only in that MEMBER module
- The same prototypes may be placed in multiple MEMBER modules to make them available in each
- Example:
MEMBER('Sample')
MAP
ComputeIt PROCEDURE
HelperProc PROCEDURE(LONG pValue)
END
ComputeIt PROCEDURE
LOC:Var LONG
CODE
! Implementation- Declared within a PROCEDURE
- Prototypes are available only within that PROCEDURE
- Example:
MyProc PROCEDURE()
MAP
LocalHelper PROCEDURE(STRING pData)
END
CODE
LocalHelper('test')- MODULE declares an external source module that contains procedure definitions
- Used to reference procedures from external libraries or separate compilation units
-
MODULE termination rules:
-
Inside MAP: MODULE must be terminated with
END - Inside CLASS: MODULE does NOT require a terminator
-
Inside MAP: MODULE must be terminated with
- Syntax:
MAP
MODULE('library_name')
ExternalProc PROCEDURE([parameters])
END ! MODULE END required inside MAP
END ! MAP END MyClass CLASS
MODULE('library_name')
ExternalProc PROCEDURE([parameters])
! No END for MODULE inside CLASS
END ! CLASS END only-
BUILTINS.CLW: Automatically included in every PROGRAM's MAP structure by the compiler
- Contains prototypes of most Clarion internal library procedures
- Makes the compiler more efficient by not having these built-in
-
EQUATES.CLW: Also automatically included
- Contains constant EQUATEs used by BUILTINS.CLW
- This is why a MAP structure is mandatory for any non-trivial Clarion program
! File: sample.cla
PROGRAM
MAP
LoadIt PROCEDURE
END
GlobalData LONG
CODE
LoadIt()! File: separate file
MEMBER('Sample')
MAP
ComputeIt PROCEDURE
END
ComputeIt PROCEDURE
LOC:Var LONG
CODE
LOC:Var = 10- Control which sections of source code are included or excluded from compilation
- Use terminator strings to mark the end of a block
- Support conditional expressions based on EQUATE values
- Can be nested up to 8 levels deep (plus one additional level for omitted code)
- IMPORTANT: Not compatible with ABC Application Class Parser
- Excludes a block of source code from compilation
- Syntax:
OMIT(terminator [,expression]) - The entire line containing the terminator is included in the OMIT block
- Block executes (code is omitted) when expression evaluates to TRUE
- If expression contains undefined EQUATE, it's assumed to be 0
-
CASE-SENSITIVE: The terminator string must match exactly (including case)
- Unlike Clarion keywords (which are case-insensitive), terminator strings use exact string comparison
-
'**END**'will NOT match**end**or**End**
-
SUBSTRING SEARCH: The terminator can appear anywhere on the line
- Doesn't need to start at column 0
- Can be in code, comments, or standalone
- Can have other text before or after it
- The entire terminating line is included in the OMIT/COMPILE block
- Examples:
OMIT('**END**')
code here
**END** ! ✅ Valid - exact match
OMIT('**END**')
code here
! **END** ! ✅ Valid - in comment
OMIT('**END**')
code here
SomeCode() **END** ! ✅ Valid - after code
OMIT('**END**')
code here
**end** ! ❌ INVALID - case mismatch
OMIT('EndOfFile')
code here
! EndOfFile ! ✅ Valid - string found in comment! Unconditional OMIT (always omit the block)
OMIT('**END**')
! This code is never compiled
Message('Debug Info')
**END**
! Conditional OMIT (omit only if _WIDTH32_ is true)
OMIT('***',_WIDTH32_)
SIGNED EQUATE(SHORT) ! Only for 16-bit
***
! Complex conditional with comparison operator
Demo EQUATE(0)
OMIT('EndDemoChk',Demo = 0) ! Omit when Demo is off
DO DemoCheck
! EndDemoChk
! Conditional OMIT with undefined EQUATE
OMIT('EndOfFile',OnceOnly) ! Compiles first time (OnceOnly assumed 0)
! Code here compiled first pass only
OnceOnly EQUATE(1) ! Defined after OMIT
! EndOfFileA class .inc guards itself against multiple inclusion by OMITting its own body when a marker EQUATE is already defined — the standard pattern produced by class templates (!ABCIncludeFile):
! File: T2.INC - class header
OMIT('_EndOfInclude_',_T2Present_)
_T2Present_ EQUATE(1)
ClassName CLASS,TYPE,MODULE('T2.CLW'),LINK('T2.CLW')
DoIt PROCEDURE()
END
_EndOfInclude_The first inclusion defines _T2Present_; every later inclusion OMITs the whole body. This complements ,ONCE — the guard also protects when the same header is reached through different paths or modules.
When a class is split across files, the implementation module ties back to its own header:
! File: T2.CLW - class implementation
MEMBER
INCLUDE('T2.INC'),ONCE
ClassName.DoIt PROCEDURE()
CODE- Includes a block of source code in compilation (opposite of OMIT)
- Syntax:
COMPILE(terminator [,expression]) - Block executes (code is compiled) when expression evaluates to TRUE
- Without expression parameter, COMPILE is redundant (all code compiles by default)
- If expression contains undefined EQUATE, it's assumed to be 0
! Conditional COMPILE (compile only if _WIDTH32_ is true)
COMPILE('***',_WIDTH32_)
SIGNED EQUATE(LONG) ! Only for 32-bit
UNSIGNED EQUATE(ULONG)
***
! Complex conditional expression
Demo EQUATE(1)
COMPILE('EndDemoChk',Demo = 1) ! Compile when Demo is on
DO DemoCheck
! EndDemoChkIn real projects the controlling symbol usually is not an EQUATE in source but a project define — set under Project Properties → Defines (DefineConstants in the .cwproj, e.g. Demo=>1) so builds switch configurations without editing source.
Both OMIT and COMPILE support these comparison operators:
-
<equate>- Check if EQUATE is non-zero -
<equate> = <integer>- Equal to -
<equate> <> <integer>- Not equal to -
<equate> > <integer>- Greater than -
<equate> < <integer>- Less than -
<equate> >= <integer>- Greater than or equal -
<equate> <= <integer>- Less than or equal
! Outer COMPILE for 32-bit builds
COMPILE ('**32bit**',_width32_)
! Inner COMPILE for debug mode
COMPILE ('*debug*',_debug_)
DEBUGGER::BUTTONLIST Equate('&Continue|&Halt|&Debug')
!end- COMPILE ('*debug*',_debug_)
! Inner OMIT for debug mode (opposite logic)
OMIT ('*debug*',_debug_)
DEBUGGER::BUTTONLIST Equate('&Continue|&Halt')
!end- OMIT ('*debug*',_debug_)
!end- COMPILE ('**32bit**',_width32_)
! Alternative OMIT for non-32-bit builds
OMIT ('**32bit**',_width32_)
DEBUGGER::BUTTONLIST Equate('&Continue|&Halt')
!end- OMIT ('**32bit**',_width32_)
RETURN- CLASS and INTERFACE are structure declarations that group data members and method prototypes
- Similar to MAP, they contain member/method declarations, NOT implementations
- Used for object-oriented programming in Clarion
- Must be terminated with END or dot (.)
Definition:
ClassName Class(), type, [attributes]
! Data members
Field1 Type
Field2 Type
! Method prototypes
Method1 PROCEDURE([parameters])
Method2 PROCEDURE([parameters]),ReturnType
EndKey Rules:
-
Termination: CLASS structures MUST be terminated with
ENDor. - Method Declarations: PROCEDURE tokens inside CLASS are prototypes, NOT implementations
- Scope Boundary: PROCEDURE/METHOD declarations inside CLASS do NOT start new scope boundaries
-
MODULE Attribute: CLASS can have MODULE attribute:
Class(), Module('file.clw') - No CODE: CLASS structures cannot contain CODE sections - only declarations
- Members Before END: All members and methods must be declared before the terminating END
Simple CLASS:
Person Class(), type
Name STRING(50)
Age LONG
GetName PROCEDURE(),STRING
SetAge PROCEDURE(LONG pAge)
EndCLASS with MODULE (External Implementation):
StringTheory Class(), type, Module('StringTheory.clw'), Link('StringTheory.clw')
value &string,PRIVATE
streamFileName &string,PRIVATE
Append PROCEDURE(STRING pValue)
GetValue PROCEDURE(),STRING
EndINTERFACE Structure:
ILogger Interface
LogMessage PROCEDURE(STRING pMessage)
LogError PROCEDURE(STRING pError)
End| Feature | MAP | CLASS |
|---|---|---|
| Purpose | Procedure prototypes | Object definition with data + methods |
| Contains | Only PROCEDURE declarations | Data members + PROCEDURE declarations |
| Implementations | External (in MEMBER files) | Can be external (MODULE) or inline |
| Termination | Requires END | Requires END or . |
| MODULE Inside | MODULE needs END | MODULE does NOT need END |
MAP
MODULE('KERNEL32')
GetTickCount PROCEDURE(),ULONG,PASCAL,RAW,NAME('GetTickCount')
Sleep PROCEDURE(ULONG dwMilliseconds),PASCAL,RAW,NAME('Sleep')
END
MODULE('USER32')
MessageBoxA PROCEDURE(ULONG hWnd,*CSTRING lpText,*CSTRING lpCaption,ULONG uType),LONG,PASCAL,RAW,NAME('MessageBoxA')
END
MyLocalProc PROCEDURE(STRING pName)
END MAP
INCLUDE('filename.inc')
MODULE('library')
ExternalProc PROCEDURE([parameters])
END
InternalProc PROCEDURE([parameters])
END MAP
MODULE('KERNEL32')
GetTickCount PROCEDURE(),ULONG,PASCAL,RAW,NAME('GetTickCount')
END
MyLocalProc PROCEDURE(STRING pName)
END- Variables often use mixed case:
MyVariable,CustomerName - Prefixes sometimes used:
pfor parameters,loc:for local variables - Constants often use ALL_CAPS or UPPER:Lower format
- Typically 2 spaces per indent level
- Structures like LOOP, IF are indented
- Data declarations in structures are indented
- Procedures have a
CODEsection where executable statements begin - Data declarations appear before the
CODEkeyword