MCPcopy Index your code
hub / github.com/TimeAndSpaceIO/java-primitive-specializations-generator

github.com/TimeAndSpaceIO/java-primitive-specializations-generator @1.4

Chat with this repo
repository ↗ · DeepWiki ↗ · release 1.4 ↗ · + Follow
312 symbols 724 edges 55 files 20 documented · 6%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

JPSG is temporarily extracted from Koloboke tree to make new releases.

Java Primitive Specializations Generator

Java Primitive Specializations Generator (JPSG) is a template processor with the primary goal of producing code specialized for any Java primitive types and for object (generic) types, while Java doesn't have generics over primitives.

Source code of the Koloboke collections library is generated with JPSG. The difference between JPSG and similar template processing engines powering other libraries of collections for primitive types in Java (fastutil, Eclipse, Trove, HPPC - all have their own template processing engine) is that JPSG's template files can be valid Java source files that correspond to one of the specializations produced from the respective template.

This allows to make the development of templates more convenient and less error prone by configuring directories with template files as source directories in your IDE so that IDE features like autocomplete and inspections will work in the template files. On the other hand, IDE will think that there are duplicate classes in the project (because a template file's name (class name) and package are the same as in one of the generated specializations), so you will lose the ability to build the project from IDE (you will need to use Maven or Gradle every time to build the project).

This is possible because JPSG captures and replaces actual type occurrences in code instead of relying on patterns that can't be part of Java code. For example, the following file CharIntPair.java is a valid JPSG template:

/* with char|byte|short|int|long|float|double|object key
        int|byte|char|short|long|float|double|object value */
/* if !(object key object value) */ // Excludes ObjectObjectPair, because there is Pair already

interface CharIntPair/*<>*/ extends Pair<Character, Integer> {
  /* if !(object key) */ char getCharKey(); /* endif */
  /* if !(object value) */ int getIntValue(); /* endif */
}

JPSG produces the following specialization FloatObjectPair.java from the above template, along with 62 others:

interface FloatObjectPair<V> extends Pair<Float, V> {
  float getFloatKey();
}

As you can see in this example, the second thing that enables JPSG's templates to be valid Java code is that control structures such as /* if ... */ ... /* endif */ use Java comment syntax. (More on this in the section about /* if */ blocks in the tutorial below.)

The above example highlights another difference between JPSG and some other template processing engines: JPSG allows specialization for primitive types as well as object (generic) types. This makes it possible to produce all possible specializations of types such as Pair<A, B>, Map<K, V>, Triple<A, B, C>, etc. from a single source template, while with some other template processing engines usually 4 or 8 different templates are required to cover all {object, primitive} combinations across all dimensions.

Although primarily designed for specializing Java types, JPSG also supports specialization over arbitrarily defined (non Java type) "dimensions". For example, for the following template file Foo.java:

/* with Foo|Bar myDimension */
class Foo {
  void getFoo() {
    int foo = /* if Foo myDimension */0// elif Bar myDimension //1// endif */;
    return foo;
  }
}

JPSG produces Foo.java:

class Foo {
  void getFoo() {
    int foo = 0;
    return foo;
  }
}

and Bar.java:

class Bar {
  void getBar() {
    int bar = 1;
    return bar;
  }
}

Dimensions for specializing Java type options and dimensions for specializing non Java type options can be mixed in a single JPSG template file.


JPSG is available as a Gradle plugin and a Maven plugin.

Tutorial

Template files

By convention, in both Gradle and Maven JPSG plugins JPSG template files are located in src/main/javaTemplates/com/mycompany/MyShortSpecializedType.java, as well as src/test/javaTemplates/.... In other words, conventional template directories javaTemplates/ are sibling to conventional java/ directories. The package structure is reflected in the directory tree structure as well as in ordinary sources. Gradle plugin additionally supports resourceTemplates/, for example src/main/resourceTemplates/META-INF/services/com.mycompany.MyShortSpecializedType.

A template file usually begins with a /* with */ construction that defines specialization dimensions of the template. For example:

/* with char|byte|short|int|long|float|double|object key
        int|byte|char|short|long|float|double|object value */
/* license header */
package com.mypackage;
...

Here there are two dimension definitions: dimension "key" has "char|byte|short|int|long|float|double|object" options, dimension "value" has "int|byte|char|short|long|float|double|object" options. Another example:

/* with integer|long|double|obj tType long|double|integer|obj uType Immutable|Mutable mutability */
...

Here dimension "tType" has "integer|long|double|obj" options, dimension "uType" has "long|double|integer|obj" and dimension "mutability" has "Immutable|Mutable" options.

Here is the formal BNF syntax of dimension definitions:

<dimensionName> := alphanumeric string
<javaTypeOption> := "bool" "ean"? | "byte" | "char" "acter"? | "short" | "int" "eger"? |
                    "long" | "float" | "double" | "obj" "ect"?
<nonJavaTypeOption> := alphanumeric string which isn't a <javaTypeOption>
<javaTypeOptions> := ( <javaTypeOption> "|" )* <javaTypeOption>
<nonJavaTypeOptions> := ( <nonJavaTypeOption> "|" )* <nonJavaTypeOption>
<options> := (<javaTypeOptions> | <nonJavaTypeOptions>)
<dimension> := <options> " " <dimensionName>
<dimensions> := ( <dimension> " "+ )* <dimension>

The first option in the |-delimited list defined for each dimension is the source option. In the previous example, the source option of dimension "tType" is "integer", "long" of "uType" dimension and "Immutable" of "mutability" dimension.

JPSG searches for occurrences of the source options in the contents and the file name of the template file and replaces them to produce specializations corresponding to all combinations of options for all dimensions (a cartesian product), except, maybe, some combinations that are filtered. See the section about /* if */ blocks below for more info about filtering.

The file name can be customized further with a special /* define ClassName */ construction. See the section about /* define */ blocks below.

If there is no /* with */ block in the beginning of a template file, JPSG attempts to deduce dimensions from the name of the template file, based on defaultTypes configuration (available in both Gradle and Maven plugins) that defaults to "byte|char|short|int|long|float|double", i. e. all primitive numeric Java types. For example, for a template file named FloatShortIntTriple.java deduced dimension definitions will be:

float|byte|char|short|int|long|double t
short|byte|char|int|long|float|double u
int|byte|char|short|long|float|double v

JPSG doesn't deduce dimensions with non Java type options from the template file name to enforce giving these dimensions more meaningful names than "t", "u", and "v". /* with */ block should be used to specify non Java type dimensions. And if such block is present, JPSG doesn't try to deduce more dimensions from the template file name combine them with explicitly defined dimensions.

If there is no /* with */ block in the beginning of a template file and its file name doesn't include parts that correspond to java primitive type names, JPSG assumes that there are no file-wide dimensions for specialization. In this case JPSG generates exactly one output file from the template with the same name. It's not pointless because such template file could have inner generation, for example:

class PrimitiveUtils {
  /* with byte|char|short|int|long|float|double primType */

  ... some code that repeats for each primitive type

  /* endwith */
}

See the section about /* with */ blocks below.

Specialization rules

Each dimension can have either all Java type options or all non Java type options (see BNF definitions of <javaTypeOptions> and <nonJavaTypeOptions> above). That is, int|long|Foo myDimension is an illegal dimension definition for JPSG because there are both Java type options "int", "long" and a non Java type option "Foo". JPSG specializes dimensions with Java type options (also called Java type dimensions) differently than dimensions with non Java type options.

Specialization of Java type dimensions

Java type options are "bool", "boolean", "byte", "short", "char", "character", "int", "integer", "long", "float", "double", "obj", and "object". Java type dimension specifies a subset of those options. "bool"/"boolean", "char"/"character", "int"/"integer", and "obj"/"object" can't both appear in one dimension definition (but there is a difference between the short and long forms - see below.) "bool"/"boolean" and "obj"/"object" can't be source options, i. e. they can't go first in the options list of a dimension.

JPSG analyzes the template and replaces primitive type appearances in all possible forms (the following examples assume that the source option is "char" or "character"): - Primitive Java type occurrences: char value = ..., public char myFunction(); - Boxed primitive class occurrences: List<Character> myValues = ..., /** @see Character#MAX_VALUE */ - Lowercase: "char" and "character", title case: "Char" and "Character", and upper case: "CHAR" and "CHARACTER" appearances in any Java identifiers (variable names, method names, class names, block labels, etc), as well as in all other (non Java) contexts in the template file, such as in comments and inside string literals. Plural forms: "chars", "characters" are recognized. Examples: Object myChar = ..., int numChars = ..., void sortCharacters();, interface CharFunctor ..., int CHARACTER_LIMIT = ..., /** Get a char value. */.

Specialization to "obj"/"object" by default replaces both primitive Java type occurrences (char) and boxed primitive class occurrences (Character) with a single letter generic parameter: the first letter of the dimension name. For example, for dimension char|object key, template

  char getChars() { List<Character> list = getList(); return list.get(0); }

is specialized to "object" as

  K getObjects() { List<K> list = getList(); return list.get(0); }

For this reason, dimensions that have "object" or "obj" options must have names all starting with different letters.

If some primitive type occurrence or a boxed primitive class occurrence should be replaced with Object rather than a generic parameter reference, that occurrence should be prepended with a special /* raw */ modifier in the template:

  /* raw */char getChars() { List</* raw */Character> list = getList(); return list.get(0); }

is specialized to "object" as

  Object getObjects() { List<Object> list = getList(); return list.get(0); }

Choosing either of the options in "bool"/"boolean", "char"/"character", "int"/"integer", and "obj"/"object" pairs affects how the specialization for the corresponding option appears in identifiers: short getShort(); (assuming the source option is "short") could be specialized as either int getInt(); or int getInteger();, depending on whether "int" or "integer" option is specified for the dimension. If the source option also allows two potential variants (i. e. the source option is either "char", "character", "int" or "integer", because booleans and objects can't be source options), the occurrences in the template that correspond to the specified source option are recognized as neutral, other occurrences are recognized as deliberately long or short:

Source option Occurrence in template Recognized as Target option Specialization outcome
"char" CharCursor neutral "bool" BoolCursor
"char" CharCursor neutral "boolean" BooleanCursor
"char" CharacterCursor long "bool" BooleanCursor
"char" CharacterCursor long "boolean" BooleanCursor
"character" CharCursor short "bool" BoolCursor
"character" CharCursor short "boolean" BoolCursor
"character" CharacterCursor neutral "bool" BoolCursor
"character" CharacterCursor neutral "boolean" BooleanCursor

It's often convenient to choose "short" (also "char" and "byte", if the template has several Java type dimensions, such as Map, Pair, Triple, etc.) as the source option, because short is rarely used in Java code, so there will be little type occurrences that need to be excluded from specialization (see the section about /* with */ blocks below). If identifiers in different styles (getChar vs getCharacter - see above) appear in the code, choosing "char"/"character" and "int"/"integer" as source options might allow to express the specialization logic without /* if */ and /* define */ blocks.

Non Java type dimensions

Options of non Java type dimensions intended for specialization must start with a capital letter: /* with Foo|Bar myDimension */, not /* with foo|bar myDimension */ (but lowercase non Java type opt

Extension points exported contracts — how you extend this code

Option (Interface)
(no doc) [5 implementers]
core/src/main/java/io/timeandspace/jpsg/Option.java
UnaryOperator (Interface)
(no doc) [1 implementers]
core/src/main/java/io/timeandspace/jpsg/function/UnaryOperator.java
Predicate (Interface)
(no doc)
core/src/main/java/io/timeandspace/jpsg/function/Predicate.java
ForkJoinTaskShim (Interface)
(no doc)
core/src/main/java/io/timeandspace/jpsg/concurrent/ForkJoinTaskShim.kt
ForkJoinTasks (Interface)
(no doc)
core/src/main/java/io/timeandspace/jpsg/concurrent/ForkJoinTasks.kt

Core symbols most depended-on inside this repo

matcher
called by 57
core/src/main/java/io/timeandspace/jpsg/CheckingPattern.java
compile
called by 30
core/src/main/java/io/timeandspace/jpsg/RegexpUtils.java
group
called by 30
core/src/main/java/io/timeandspace/jpsg/CheckingMatcher.kt
start
called by 28
core/src/main/java/io/timeandspace/jpsg/CheckingMatcher.kt
find
called by 20
core/src/main/java/io/timeandspace/jpsg/CheckingMatcher.kt
get
called by 20
core/src/main/java/io/timeandspace/jpsg/concurrent/NoForkJoinTasks.kt
toString
called by 17
core/src/main/java/io/timeandspace/jpsg/Context.java
near
called by 15
core/src/main/java/io/timeandspace/jpsg/MalformedTemplateException.kt

Shape

Method 248
Class 55
Interface 5
Enum 4

Languages

Java62%
Kotlin38%

Modules by API surface

core/src/main/java/io/timeandspace/jpsg/Generator.kt40 symbols
gradle-plugin/src/main/java/io/timeandspace/jpsg/JpsgTask.java15 symbols
core/src/test/resources/examples/bits-modifier/generated/BitsModifierAndWrapping.java15 symbols
core/src/main/java/io/timeandspace/jpsg/PrimitiveType.java14 symbols
gradle-plugin/src/main/java/io/timeandspace/jpsg/JavaPrimitiveSpecializationsGenerationPlugin.java13 symbols
core/src/main/java/io/timeandspace/jpsg/Dimensions.kt13 symbols
core/src/main/java/io/timeandspace/jpsg/ObjectType.java11 symbols
core/src/main/java/io/timeandspace/jpsg/DefinitionProcessor.java11 symbols
core/src/main/java/io/timeandspace/jpsg/Context.java11 symbols
core/src/main/java/io/timeandspace/jpsg/CheckingMatcher.kt11 symbols
core/src/main/java/io/timeandspace/jpsg/SimpleOption.java9 symbols
core/src/main/java/io/timeandspace/jpsg/TemplateProcessor.java8 symbols

For agents

$ claude mcp add java-primitive-specializations-generator \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact