Add hexadecimal integer literals (e.g. 0xFF) - #132
Merged
Conversation
Whole numbers can now be written in hexadecimal notation, using the 0x (or 0X) prefix followed by hex digits, e.g. 0xFF for 255. This has been requested on the mailing list several times over the years (the previous workaround being to expose Integer.decode as a shared variable). The change is purely additive and backward compatible: in an expression, a sequence like "0xFF" was previously a parse error (a number immediately followed by an identifier), so no currently-valid template can be affected. The new HEX_INTEGER token is only active in the expression lexer states, never in plain template text. Hex literals specify whole numbers only; the sign can be applied as a separate operator (e.g. -0x10). There's no limit on the number of digits: the value is narrowed to an Integer if it fits into a 32-bit signed integer, to a Long if it fits into a 64-bit signed integer, and is kept as a BigInteger otherwise. Added JUnit coverage and a note in the Manual's "Direct specification of values" section (with @SInCE 2.3.35).
Contributor
|
Thanks! Also added followup commit in 2.3-gae (like I did that movement into ArithmeticEngine) |
gdisirio
added a commit
to gdisirio/freemarker-codegen
that referenced
this pull request
Sep 5, 2026
Moves the fork onto the official 2.3.35 release, which is the first one to contain the string built-ins and hexadecimal literals that were contributed upstream from this fork (PRs apache#130 and apache#132). The fork's own versions of both are dropped in favour of the released ones, which differ: ?indent(prefix) now adds the prefix to every line and right-trims each resulting line, with an optional 2nd boolean argument to switch that off. ?dedent is called without parentheses in its no-argument form, and ?dedent(prefix) removes the longest matching prefix of the prefix from every line, also trimming. Whitespace-only lines count as empty throughout. These were arrived at during review; see the PR discussions. ?right_pad_lines is gone. Aligning a column of lines is better done by splitting and looping, which keeps every single-line built-in available and, unlike a whole-block built-in, lets you append after the padding of each line (as C macro continuations require). The upstream ?lines built-in, new in 2.3.35, is what to split with. The README section is rewritten accordingly. Hexadecimal parsing moved into the new overridable ArithmeticEngine.hexadecimalToNumber, so FTL.jj delegates to it instead of parsing hex itself. The default implementation keeps the Integer/Long/ BigInteger narrowing, so oversized literals like 0xFFFFFFFFFFFFFFFF still parse exactly, which matters for masks in code-first mode. CommandLine.java is kept although 2.3.35 removed it: this fork wires it as the jar's Main-Class so that "java -jar" reports the codegen version, and a real CLI is planned. That is a deliberate divergence from upstream, which removed all main() methods because security scans flag them. version.properties now reports the upstream version as the released 2.3.35 rather than 2.3.35-nightly; codegenVersion stays 0.1.0.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Whole numbers can now be written in hexadecimal notation, using an
0x(or0X) prefix followed by hex digits —0xFFis 255,0x100is 256.This has come up on the mailing list several times over the years; the usual workaround has been exposing
Integer.decodeas a shared variable. It's most useful when templates deal with values that are conventionally written in hex — bitmasks, hardware register values, colour constants, binary protocol fields — where decimal spellings obscure the intent.Backward compatibility
Purely additive. In an expression, a sequence like
0xFFwas previously a parse error (a number immediately followed by an identifier), so no currently-valid template can change meaning. The newHEX_INTEGERtoken is only active in the expression lexer states, never in plain template text.Range and representation
Hex literals specify whole numbers only — no fractional part, no exponent, no sign. A sign can still be applied as a separate operator, as in
-0x10.Values are narrowed to the smallest of
Integer/Longthat fits, and beyondLongrange they becomeBigInteger, so there is no arbitrary limit on the number of digits and no overflow error.One consequence worth flagging explicitly: this means hex literals do not go through
ArithmeticEngine.toNumber, so under the defaultBIGDECIMAL_ENGINEthe literal255yields aBigDecimalwhile0xFFyields anInteger. The two compare and arithmetically behave identically, but they are different Java types, which is observable if the value is passed to an overloaded Java method. I went this way because hex notation is inherently integral andInteger/Longis what callers doing mask arithmetic expect — but if you'd rather hex respect the configured arithmetic engine for consistency, that's a small change and I'm happy to make it.Testing & docs
HexLiteralTest: basic values, both prefix cases, use in expressions, assignment, comparison, and the Integer/Long/BigInteger boundaries.@since 2.3.35../gradlew checkand./gradlew manualOfflineboth pass.Relationship to #130
Independent of #130 — the two share no code. This changes only the lexer/parser (
FTL.jj); #130 changes only the string built-ins (BuiltIn.java,BuiltInsForStringsBasic.java). The one file both touch isbook.xml, and the hunks are about 10,000 lines apart (the expressions chapter vs. the built-ins reference). They merge cleanly in either order, so no coordination is needed.