Repository: alecthomas/chroma
Pull request #1078 (2025-05-11 19:31:59)

Added IBM RPG lexer
Hi !

Here's a lexer for IBM RPG !
You can learn about this language here :
- https://en.wikipedia.org/wiki/IBM_RPG
- https://www.ibm.com/docs/en/i/7.6.0?topic=languages-rpg

For legacy purposes, the language can be written in two different forms, the fixed format (looks like a punched card, expects specific characters to be at certain columns in source code) and the free format (gets rid of fixed format, and looks like modern languages as Python).
This lexer only handles the free format, I may open a pull request later to implement the fixed format syntax.

There are some RPG repositories available on GitHub, you can search using "language:RPGLE" to see them.

The language is old but still maintained and even continues to evolve nowadays.
The lexer is based on the latest version at this day, RPG 7.6.

Are the MIME types in the XML files important ? I don't think this language has one (and I didn't know there were MIME types for programming languages), so I made one which looks like what I saw in other lexers XML files.

And the language can have embedded SQL statements in the source code, so I reused the SQL lexer. Please tell me if there's a better way to do it.

The language is rather tied to IBM environment, so if you're interested, here's how to try it :
- https://pub400.com/ provides free accounts to an IBM machine to try and discover it
- https://www.nicklitten.com/use-a-5250-green-screen-terminal-in-visual-studio/ to get a terminal to access pub400 machine and run code
- https://www.nicklitten.com/connecting-to-pub400-with-vs-code-for-ibm-i/ shows how to code RPG in VSCode and compile it


Patch:
diff --git a/lexers/embedded/rpgle.xml b/lexers/embedded/rpgle.xml
new file mode 100644
index 0000000..ca9a74e
--- /dev/null
+++ b/lexers/embedded/rpgle.xml
@@ -0,0 +1,155 @@
+<lexer>
+  <config>
+    <name>RPGLE</name>
+    <alias>SQLRPGLE</alias>
+    <alias>RPG IV</alias>
+    <filename>*.RPGLE</filename>
+    <filename>*.rpgle</filename>
+    <filename>*.SQLRPGLE</filename>
+    <filename>*.sqlrpgle</filename>
+    <mime_type>text/x-rpgle</mime_type>
+    <mime_type>text/x-sqlrpgle</mime_type>
+    <case_insensitive>true</case_insensitive>
+  </config>
+  <rules>
+    <state name="root">
+      <rule pattern="\/\/.*">
+        <token type="Comment"/>
+      </rule>
+      <rule pattern="^\*\*free$">
+        <token type="CommentSpecial"/>
+      </rule>
+      <rule pattern="(\*(all(g|oc|ow|sep|thread|u|x)?|altseq|alwblanknum|ascii|astfill|aut[lo]|basic|blank(s)?|caller|cancl|cdmy(0)?|change|char|cl|cmd(y|y0)?|cnowiden|cntrld|col|comp(at)?|concurrent|constants|constructor|convert|copyright|crt(bndrpg|rpgmod)|ct(data|lbdy|lspec)|cur(lib|sym)|cvt(data)?|cwiden|cymd(0)?|data|date(time)?|day(s)?|dclcase|ddm|debugio|dec|delete|det[cl]|dft|diag|dmy|dtaara|dump|end|entry(exit)?|equate|escape|eur(0)?|exact|exclude|excp|exp(dds|include)|ext(desc|dft)?|file(s)?|first|ftrans|full|gen|getin|graph(ic)?|hex|hival|hms|hours|ignore|ilerpg|in([0-9]{2}|H[1-9]|K[A-Y]|L[1-9]|LR|O[AGV]|U[1-8])?|INFO|INHERIT|INIT|INP(UT(ONLY|PACKED)?)?|INZ(OFL|SR)|ISO(0)?|JAVA|JIS|JOB(RUN(0|MIX|_DBCS|_JAVA|_MIXED)?)?|JUL|KEEP|KEY|LANGID(SHR|UNQ)|LDA|LGL|LIB(L|RCRTAUT)|LIKE(DS)?|LIST|LOCK|LONG(JOBRUN(0)?|JUL(0)?)|LOVAL|LVL[12]|MAX(DIGITS)?|MDY(0)?|MINUTES|MIXED(ASCII|EBCDIC)|MN|MODE|MODULE|MONTH(S)?|MS(ECONDS)?|M[A-Z]{3}[0-9]{4}|NATURAL|NEW|NEXT|NO(ALLOW|CHGDSLEN|COL|CVT(DATA)?|DATETIME|DEBUGIO|EXACT|EXPDDS|EXPINCLUDE|EXT|GEN|GRAPHIC|IND|INPUTPACKED|INZOFL|KEY|PASS|RMVCOMMENT|SECLVL|SHOWCPY|SHOWSKP|SRCSTMT|UNREF|VARCHAR|VARGRAPHIC|XREF|ZONED)?|NONE|NOTIFY|NULL(IND)?|OFF|OFL|OMIT|ON(EXIT|LY)?|OPCODE|OUT(PUT)?|OWNER|PARM(S)?|PCML|PDA|PEP|PGM(BDY)?|PLACE|PROC|PROGRAM|PSSR|RECORD|REQUIRE|RESDECPOS|RETVAL|RIGHTADJ|RMVCOMMENT|ROUTINE|SECLVL|SECONDS|SELF|SERIALIZE|SHOW(CPY|SKP)|SIZE|SNGLVL|SRC(MBRTXT|STMT)?|START|STATUS|STDCHARSIZE|STGMDL|STRICTKEYS|STRING|SYS|TBL|TERASPACE|TERM|THREAD_(CONCURRENT|SERIALIZE)|TOT[CL]|TRIM|UCS2|UNIQUE|UNREF|UNSET|UPDATE|USA(0)?|USE(DECEDIT)?|USER|USR(CTL|SPC)|UTF(8|16)?|V[0-9]R[0-9]M[0-9]|V[678]|VAR(CHAR|GRAPHIC|SIZE)?|WARN|XMLSAX|XML_(ATTR_(CHARS|NAME|PREDEF_REF|UCS2_REF)|CHARS|COMMENT|DOCTYPE_DECL|ENCODING_DECL|END_(ATTR|CDATA|DOCUMENT|ELEMENT)|EXCEPTION|PI_(DATA|TARGET)|PREDEF_REF|STANDALONE_DECL|START_(CDATA|DOCUMENT|ELEMENT)|UCS2_REF|UNKNOWN_(ATTR_REF|REF)|VERSION_INFO)|XREF|YEAR(S)?|YES|YMD(0)?|ZERO(S)?|ZONED|[DHMNSY]))\b">
+        <token type="Literal"/> <!-- Special values -->
+      </rule>
+      <rule pattern="\bexec\s+sql\b"> <!-- embedded SQL -->
+        <token type="Keyword"/>
+        <push state="exec-sql"/>
+      </rule>
+      <rule pattern="(%(abs|addr|alloc|bitand|bitnot|bitor|bitxor|char|charcount|check|checkr|concat|concatarr|data|date|days|dec|dech|decpos|diff|div|editc|editflt|editw|elem|eof|equal|error|fields|float|found|gen|graph|handler|hival|hours|int|inth|kds|left|len|list|lookup(lt|ge|gt|le)?|loval|lower|max|maxarr|min|minutes|minarr|months|mseconds|msg|nullind|occur|omitted|open|paddr|parms|parmnum|parser|passed|proc|range|realloc|rem|replace|right|scan|scanr|scanrpl|seconds|shtdn|size|split|sqrt|status|str|subarr|subdt|subst|target|this|time|timestamp|tlookup(lt|ge|gt|le)?|trim(l|r)?|ucs2|uns|unsh|upper|xfoot|xlate|xml|years))\b">
+        <token type="NameFunctionMagic"/> <!-- builtin functions -->
+      </rule>
+      <rule pattern="\b(ind|int|uns|(var)?char|bindec|float|packed|zoned|time(stamp)?|date|pointer|clob|blob|(var)?graph|object|(var)?ucs2)\b">
+        <token type="KeywordType"/> <!-- variable types -->
+      </rule>
+      <rule pattern="\b(dcl\-(ds|pi|proc|pr|[csf])|end\-(ds|pi|proc|pr)|ctl\-opt|const|value|to|downto|not)\b">
+        <token type="Keyword"/> <!-- Syntax keywords -->
+      </rule>
+      <rule pattern="\b(acq|add|adddur|alloc|and(gt|lt|eq|ne|ge|le)?|bitoff|biton|cab(gt|lt|eq|ne|ge|le)?|call(b|p)?|cas(gt|lt|eq|ne|ge|le)?|cat|chain|check|checkr|clear|close|commit|comp|data-gen|data-into|dealloc|define|delete|div|do((u|w)(gt|lt|eq|ne|ge|le)?)?|dsply|dump|else(if)?|end(cs|do|for|if|mon|sl|sr)?|eval(r|-corr)?|except|exfmt|exsr|extrct|feod|for(-each)?|force|goto|if(gt|lt|eq|ne|ge|le)?|in|iter|kfld|klist|leave(sr)?|lookup|m(hh|hl|lh|ll)zo|monitor|move[al]?|mult|mvr|next|occur|on-(error|excp|exit)|open|or(gt|lt|eq|ne|ge|le)?|other|out|parm|plist|post|read(c|e|p|pe)?|realloc|rel|reset|return|rolbk|scan|select|set(gt|ll|off|on)|shtdn|snd-msg|sorta|sqrt|sub(dur|st)?|tag|test[bnz]?|time|unlock|update|when(gt|lt|eq|ne|ge|le)?|when-i[ns]|write|xfoot|slate|xml-(into|sax)|z-(add|sub))\b">
+        <token type="Keyword"/> <!-- Operation codes -->
+      </rule>
+      <rule pattern="\b(alloc|altseq|ccsid|copy(nest|right)|cursym|dat(edit|fmt)|debug|dec(edit|prec)|dftname|expropts|extbinint|fltdiv|formsalign|ftrans|intprec|(no)?main|openopt|thread|timfmt|actgrp|alwnull|aut|bnddir|cvtopt|dateyy|dftactgrp|enbpfrcol|fixnbr|genlvl|indent|langid|optimize|option|pgminfo|prfdta|reqprexp|strseq|stgmdl|text|truncnbr|usrprf)\b">
+        <token type="KeywordReserved"/> <!-- ctl-opt (former H spec) -->
+      </rule>
+      <rule pattern="\b(alias|align|alt|altseq|ascend|based|ccsid|class|const|ctdata|datfmt|descend|dim|dtaara|export|ext(fld|fmt|name|pgm|proc)?|fromfile|import|inz|len|like(ds|file|rec)?|noopt|nullind|occurs|opdesc|options|overlay|overload|packeven|perrcd|pos|prefix|procptr|psds|qualified|reqproto|rtnparm|samepos|static|template|timfmt|tofile|value|varying)\b">
+        <token type="KeywordReserved"/> <!-- Other keywords (not operation codes nor ctl-opt/H spec) -> mostly used with variables -->
+      </rule>
+      <rule pattern="(/(copy|define|eject|else|elseif|end-exec|end-free|endif|eof|exec|free|if|include|restore|set|space|title|undefine))\b">
+        <token type="CommentPreproc"/> <!-- Preprocessor instructions -->
+      </rule>
+      <rule pattern="\b([0-9]+((\.[0-9]?)(e[+-][0-9]+)?)?)\b">
+        <token type="LiteralNumber"/>
+      </rule>
+      <rule pattern="\bx'[0-9a-fA-F]*'">
+        <token type="LiteralString"/> <!-- Hex string -->
+      </rule>
+      <rule pattern="'(''|[^'])*'">
+        <token type="LiteralString"/>
+      </rule>
+      <rule pattern="[,:;\(\)]+">
+        <token type="Punctuation"/>
+      </rule>
+      <rule pattern="((\*\*)|(&lt;&gt;)|=|([&lt;&gt;\+\-\*\/]=?))+">
+        <token type="Operator"/> <!-- Arithmetic operators -->
+      </rule>
+      <rule pattern="\s">
+        <token type="TextWhitespace"/>
+      </rule>
+      <rule pattern=".">
+        <token type="Text"/>
+      </rule>
+    </state>
+    <state name="exec-sql">
+      <rule pattern=';'>
+        <token type="Punctuation"/>
+        <pop depth="1"/>
+      </rule>
+      <rule pattern="\s+">
+        <token type="TextWhitespace"/>
+      </rule>
+      <rule pattern="--.*\n?">
+        <token type="CommentSingle"/>
+      </rule>
+      <rule pattern="/\*">
+        <token type="CommentMultiline"/>
+        <push state="sql-multiline-comments"/>
+      </rule>
+      <rule pattern="&#39;">
+        <token type="LiteralStringSingle"/>
+        <push state="sql-string"/>
+      </rule>
+      <rule pattern="&#34;">
+        <token type="LiteralStringDouble"/>
+        <push state="sql-double-string"/>
+      </rule>
+      <rule pattern="(DATETIME_INTERVAL_PRECISION|PARAMETER_SPECIFIC_CATALOG|PARAMATER_ORDINAL_POSITION|USER_DEFINED_TYPE_CATALOG|PARAMATER_SPECIFIC_SCHEMA|TRANSACTIONS_ROLLED_BACK|USER_DEFINED_TYPE_SCHEMA|PARAMETER_SPECIFIC_NAME|DATETIME_INTERVAL_CODE|TRANSACTIONS_COMMITTED|USER_DEFINED_TYPE_NAME|CHARACTER_SET_CATALOG|DYNAMIC_FUNCTION_CODE|COMMAND_FUNCTION_CODE|RETURNED_OCTET_LENGTH|MESSAGE_OCTET_LENGTH|CHARACTER_SET_SCHEMA|CONSTRAINT_CATALOG|TRANSACTION_ACTIVE|CHARACTER_SET_NAME|CURRENT_TIMESTAMP|CONSTRAINT_SCHEMA|COLLATION_CATALOG|RETURNED_SQLSTATE|DYNAMIC_FUNCTION|CONDITION_NUMBER|CHARACTER_LENGTH|COMMAND_FUNCTION|COLLATION_SCHEMA|CHARACTERISTICS|TRIGGER_CATALOG|CONNECTION_NAME|SUBCLASS_ORIGIN|RETURNED_LENGTH|TIMEZONE_MINUTE|CONSTRAINT_NAME|ROUTINE_CATALOG|TRIGGER_SCHEMA|ROUTINE_SCHEMA|LOCALTIMESTAMP|IMPLEMENTATION|PARAMATER_NAME|MESSAGE_LENGTH|PARAMETER_MODE|COLLATION_NAME|TIMEZONE_HOUR|SPECIFIC_NAME|DETERMINISTIC|CORRESPONTING|AUTHORIZATION|INSTANTIABLE|CURRENT_TIME|CURRENT_USER|ROUTINE_NAME|NOCREATEUSER|MESSAGE_TEXT|SQLEXCEPTION|CATALOG_NAME|SESSION_USER|CLASS_ORIGIN|CURRENT_ROLE|SPECIFICTYPE|SERIALIZABLE|CURRENT_DATE|OCTET_LENGTH|CURRENT_PATH|TRIGGER_NAME|CHAR_LENGTH|SYSTEM_USER|REFERENCING|UNENCRYPTED|COLUMN_NAME|SQLWARNINIG|DIAGNOSTICS|CURSOR_NAME|SERVER_NAME|INSENSITIVE|SCHEMA_NAME|UNCOMMITTED|TRANSACTION|CONSTRUCTOR|LANCOMPILER|CARDINALITY|CONSTRAINTS|TRANSLATION|CHECKPOINT|CONSTRAINT|CONNECTION|PRIVILEGES|COMPLETION|CONVERSION|DELIMITERS|TABLE_NAME|INDITCATOR|INITIALIZE|DESCRIPTOR|REPEATABLE|CREATEUSER|DEFERRABLE|DESTRUCTOR|PROCEDURAL|DICTIONARY|DISCONNECT|TRANSFORMS|KEY_MEMBER|BIT_LENGTH|ASYMMETRIC|ASSIGNMENT|ASENSITIVE|OVERRIDING|PARAMETERS|REFERENCES|ORDINALITY|NOCREATEDB|STATISTICS|DEALLOCATE|SAVE_POINT|RECURSIVE|STRUCTURE|SUBSTRING|IMMEDIATE|GENERATED|SYMMETRIC|STATEMENT|INCREMENT|IMMUTABLE|INCLUDING|COMMITTED|TEMPORARY|INITIALLY|TERMINATE|PRECISION|DELIMITER|TIMESTAMP|INTERSECT|ISOLATION|TRANSFORM|TRANSLATE|ROW_COUNT|ASSERTION|PARAMETER|EXCLUSIVE|LOCALTIME|VALIDATOR|AGGREGATE|EXCLUDING|SENSITIVE|EXCEPTION|ENCRYPTED|OPERATION|HIERARCHY|COLLATION|PROCEDURE|CONTINUE|ENCODING|MINVALUE|SPECIFIC|ABSOLUTE|SECURITY|WHENEVER|EXISTING|VOLATILE|MAXVALUE|EXTERNAL|NULLABLE|VARIABLE|SQLERROR|DISTINCT|DISPATCH|END-EXEC|LOCATION|ALLOCATE|OVERLAPS|UNLISTEN|ROLLBACK|TRUNCATE|DESCRIBE|SQLSTATE|BACKWARD|FUNCTION|LANGUAGE|KEY_TYPE|CASCADED|POSITION|TRAILING|DEFERRED|RELATIVE|DEFAULTS|COALSECE|PREORDER|GROUPING|MODIFIES|INHERITS|PRESERVE|DATABASE|RESTRICT|IDENTITY|TEMPLATE|NATIONAL|CONTAINS|CREATEDB|IMPLICIT|OPERATOR|CONVERT|CURRENT|CONNECT|RECHECK|PRIMARY|STORAGE|DECLARE|DEFAULT|HANDLER|COLLATE|PREPARE|REINDEX|GRANTED|CHECKED|POSTFIX|REPLACE|INSTEAD|CATALOG|RESTART|INVOKER|PLACING|PENDANT|DEFINED|ITERATE|PARTIAL|CASCADE|BREADTH|GENERAL|TRIGGER|SESSION|BETWEEN|DEFINER|LATERAL|LEADING|RETURNS|TRUSTED|UNKNOWN|FORWARD|UNNAMED|OVERLAY|FORTRAN|ANALYZE|OPTIONS|ANALYSE|FOREIGN|ROUTINE|LOCATOR|DESTROY|SUBLIST|VERBOSE|EXTRACT|NOTNULL|EXPLAIN|VERSION|SQLCODE|EXECUTE|NOTHING|DYNAMIC|WITHOUT|SIMILAR|NATURAL|COMMENT|CLUSTER|PASCAL|SOURCE|EQUALS|CALLED|ESCAPE|EXCEPT|SELECT|ISNULL|DOMAIN|SEARCH|SCROLL|SIMPLE|BITVAR|MINUTE|EXISTS|SCHEMA|ATOMIC|METHOD|NOTIFY|ACCESS|UNIQUE|ROLLUP|NULLIF|OBJECT|STABLE|COLUMN|REVOKE|OFFSET|COMMIT|MODIFY|FREEZE|DELETE|RETURN|RESULT|UNNEST|OPTION|GLOBAL|VALUES|RENAME|SYSTEM|STATIC|UPDATE|OUTPUT|LISTEN|STDOUT|STRICT|PUBLIC|IGNORE|PREFIX|SECOND|CREATE|LENGTH|BEFORE|HAVING|INSERT|VACUUM|CURSOR|ELSIF|USING|ALTER|STYPE|CYCLE|LARGE|INPUT|CROSS|INOUT|INNER|INFIX|INDEX|LEVEL|USAGE|ILIKE|VALID|OWNER|GRANT|READS|UPPER|LIMIT|OUTER|STDIN|SYSID|GROUP|ALIAS|ORDER|UNTIL|LOCAL|RESET|START|COUNT|LOWER|TABLE| TEMP|PRIOR|AFTER|STATE|ADMIN|RIGHT|COBOL|FOUND|MATCH|FORCE|ABORT|FIRST|FINAL|CLOSE|DEREF|FETCH|WHERE|FALSE|SCALE|BEGIN|CLASS|TOAST|WRITE|NCLOB|NCHAR|CHECK|CHAIN|SPACE|NAMES|EVERY|MUMPS|CACHE|UNION|UNDER|SETOF|MONTH|SHARE|SCOPE|TREAT|SHOW|SIZE|SOME|SETS|SELF|ELSE|EACH|DROP|TYPE|FROM|RULE|DESC|ROWS|ZONE|ROLE|TRUE|FREE|FULL|GOTO|TRIM|HOLD|HOST|DATA|READ|INTO|USER|JOIN|CUBE|LAST|LEFT|LESS|LIKE|LOAD|LOCK|OPEN|COPY|ONLY|OIDS|VIEW|WHEN|THAN|THEN|NULL|NONE|WITH|WORK|NEXT|YEAR|MODE|CAST|CASE|MOVE|CALL|MORE|BOTH|EXEC|CLOB|OUT|MOD|ARE|SUM|DAY|GET|AVG|NEW|SQL|ABS|MIN|ASC|END|ROW|NOT|FOR|ANY|PLI|MAX|REF|MAP|ADA|KEY|AND|ADD|ALL|OLD|OFF|PAD|SET|OR|ON|TO|IS|OF|IN|IF|GO|AS|DO|AT|NO|BY|C|G)\b">
+        <token type="Keyword"/>
+      </rule>
+      <rule pattern="(CHARACTER|SMALLINT|INTERVAL|DECIMAL|SERIAL8|VARYING|BOOLEAN|VARCHAR|INTEGER|NUMERIC|SERIAL|BINARY|BIGINT|NUMBER|FLOAT|ARRAY|TEXT|REAL|INT8|DATE|CHAR|BLOB|DEC|BIT|INT)\b">
+        <token type="NameBuiltin"/>
+      </rule>
+      <rule pattern="[+*/&lt;&gt;=~!@#%^&amp;|`?-]">
+        <token type="Operator"/>
+      </rule>
+      <rule pattern="[0-9]+">
+        <token type="LiteralNumberInteger"/>
+      </rule>
+      <rule pattern="[a-z_][\w$]*">
+        <token type="Name"/>
+      </rule>
+      <rule pattern="[;:()\[\],.]">
+        <token type="Punctuation"/>
+      </rule>
+    </state>
+    <state name="sql-multiline-comments">
+      <rule pattern="/\*">
+        <token type="CommentMultiline"/>
+        <push state="sql-multiline-comments"/>
+      </rule>
+      <rule pattern="\*/">
+        <token type="CommentMultiline"/>
+        <pop depth="1"/>
+      </rule>
+      <rule pattern="[^/*]+">
+        <token type="CommentMultiline"/>
+      </rule>
+      <rule pattern="[/*]">
+        <token type="CommentMultiline"/>
+      </rule>
+    </state>
+    <state name="sql-string">
+      <rule pattern="[^&#39;]+">
+        <token type="LiteralStringSingle"/>
+      </rule>
+      <rule pattern="&#39;&#39;">
+        <token type="LiteralStringSingle"/>
+      </rule>
+      <rule pattern="&#39;">
+        <token type="LiteralStringSingle"/>
+        <pop depth="1"/>
+      </rule>
+    </state>
+    <state name="sql-double-string">
+      <rule pattern="[^&#34;]+">
+        <token type="LiteralStringDouble"/>
+      </rule>
+      <rule pattern="&#34;&#34;">
+        <token type="LiteralStringDouble"/>
+      </rule>
+      <rule pattern="&#34;">
+        <token type="LiteralStringDouble"/>
+        <pop depth="1"/>
+      </rule>
+    </state>
+  </rules>
+</lexer>
diff --git a/styles/rpgle.xml b/styles/rpgle.xml
new file mode 100644
index 0000000..2221d68
--- /dev/null
+++ b/styles/rpgle.xml
@@ -0,0 +1,29 @@
+<style name="RPGLE">
+  <entry type="Error" style="#960050 bg:#1e0010"/>
+  <entry type="Background" style="bg:#fafafa"/>
+  <entry type="Keyword" style="#00a8c8"/>
+  <entry type="KeywordNamespace" style="#f92672"/>
+  <entry type="KeywordReserved" style="#ffff00"/>
+  <entry type="KeywordType" style="#800000"/>
+  <entry type="Name" style="#111111"/>
+  <entry type="NameAttribute" style="#75af00"/>
+  <entry type="NameClass" style="#75af00"/>
+  <entry type="NameConstant" style="#00a8c8"/>
+  <entry type="NameDecorator" style="#75af00"/>
+  <entry type="NameException" style="#75af00"/>
+  <entry type="NameFunction" style="#75af00"/>
+  <entry type="NameOther" style="#75af00"/>
+  <entry type="NameTag" style="#f92672"/>
+  <entry type="Literal" style="#ae81ff"/>
+  <entry type="LiteralDate" style="#d88200"/>
+  <entry type="LiteralString" style="#d88200"/>
+  <entry type="LiteralStringEscape" style="#8045ff"/>
+  <entry type="LiteralNumber" style="#ae81ff"/>
+  <entry type="Operator" style="#f92672"/>
+  <entry type="Punctuation" style="#ff0000"/>
+  <entry type="Comment" style="#75715e"/>
+  <entry type="CommentPreproc" style="#2e7d32"/>
+  <entry type="GenericEmph" style="italic"/>
+  <entry type="GenericStrong" style="bold"/>
+  <entry type="Text" style="#272822"/>
+</style>


Test patch:
diff --git a/lexers/testdata/rpgle/simple_free_form.actual b/lexers/testdata/rpgle/simple_free_form.actual
new file mode 100644
index 0000000..775a406
--- /dev/null
+++ b/lexers/testdata/rpgle/simple_free_form.actual
@@ -0,0 +1,12 @@
+**free
+
+dcl-pi *n;
+    n int(10) const;
+end-pi;
+
+dcl-s myvar varchar(52) inz('hello world') ccsid(*utf8);
+
+myvar += %char(n);
+
+dsply myvar;
+return;
\ No newline at end of file
diff --git a/lexers/testdata/rpgle/simple_free_form.expected b/lexers/testdata/rpgle/simple_free_form.expected
new file mode 100644
index 0000000..1f255ae
--- /dev/null
+++ b/lexers/testdata/rpgle/simple_free_form.expected
@@ -0,0 +1,57 @@
+[
+  {"type":"CommentSpecial","value":"**free"},
+  {"type":"TextWhitespace","value":"\n\n"},
+  {"type":"Keyword","value":"dcl-pi"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Literal","value":"*n"},
+  {"type":"Punctuation","value":";"},
+  {"type":"TextWhitespace","value":"\n    "},
+  {"type":"Text","value":"n"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"KeywordType","value":"int"},
+  {"type":"Punctuation","value":"("},
+  {"type":"LiteralNumber","value":"10"},
+  {"type":"Punctuation","value":")"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Keyword","value":"const"},
+  {"type":"Punctuation","value":";"},
+  {"type":"TextWhitespace","value":"\n"},
+  {"type":"Keyword","value":"end-pi"},
+  {"type":"Punctuation","value":";"},
+  {"type":"TextWhitespace","value":"\n\n"},
+  {"type":"Keyword","value":"dcl-s"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"myvar"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"KeywordType","value":"varchar"},
+  {"type":"Punctuation","value":"("},
+  {"type":"LiteralNumber","value":"52"},
+  {"type":"Punctuation","value":")"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"KeywordReserved","value":"inz"},
+  {"type":"Punctuation","value":"("},
+  {"type":"LiteralString","value":"'hello world'"},
+  {"type":"Punctuation","value":")"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"KeywordReserved","value":"ccsid"},
+  {"type":"Punctuation","value":"("},
+  {"type":"Literal","value":"*utf8"},
+  {"type":"Punctuation","value":");"},
+  {"type":"TextWhitespace","value":"\n\n"},
+  {"type":"Text","value":"myvar"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Operator","value":"+="},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"NameFunctionMagic","value":"%char"},
+  {"type":"Punctuation","value":"("},
+  {"type":"Text","value":"n"},
+  {"type":"Punctuation","value":");"},
+  {"type":"TextWhitespace","value":"\n\n"},
+  {"type":"Keyword","value":"dsply"},
+  {"type":"TextWhitespace","value":" "},
+  {"type":"Text","value":"myvar"},
+  {"type":"Punctuation","value":";"},
+  {"type":"TextWhitespace","value":"\n"},
+  {"type":"Keyword","value":"return"},
+  {"type":"Punctuation","value":";"}
+]