Skip to content

Commit

Permalink
Add aggressiveFloatParsing
Browse files Browse the repository at this point in the history
  • Loading branch information
smwhr committed Feb 8, 2025
1 parent c556321 commit 968b236
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 11 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,19 @@ console.log(inkStory.ContinueMaximally());

From there on, you can follow [the official documentation](https://github.com/inkle/ink/blob/master/Documentation/RunningYourInk.md#getting-started-with-the-runtime-api).

#### aggressive float parsing
Due to the nature of number in javascript, when loading a json file created from Inky (or inklecate), if floating point numbers that look like integer exists in you ink story (eg: something like `{5.0}`), they won't be understood correctly and that can lead to computation and rounding errors. (eg: `{7/3.0}` will display `2` instead of `2.33`).

To correct this, you can load your story with `aggressiveFloatParsing` turned on :

```javascript
var inkStory = new Story(json, {aggressiveFloatParsing: true});

console.log(inkStory.ContinueMaximally());
//etc
```
**Caveat** : Use with caution, if you have floating point numbers _in your text_, they may be altered.

## Differences with the C# API

There are a few very minor API differences between ink C# and inkjs:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "inkjs",
"version": "2.3.1",
"version": "2.3.2",
"description": "A javascript port of inkle's ink scripting language (http://www.inklestudios.com/ink/)",
"type": "commonjs",
"main": "dist/ink-full.js",
Expand Down
6 changes: 6 additions & 0 deletions src/engine/JsonSerialisation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,12 @@ export class JsonSerialisation {
if (typeof token === "string") {
let str = token.toString();

//Explicit float value of the form "123.00f"
const floatRepresentation = /^([0-9]+.[0-9]+f)$/.exec(str);
if (floatRepresentation) {
return new FloatValue(parseFloat(floatRepresentation[0]));
}

// String value
let firstChar = str[0];
if (firstChar == "^") return new StringValue(str.substring(1));
Expand Down
29 changes: 23 additions & 6 deletions src/engine/SimpleJson.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,34 @@
export class SimpleJson {
public static TextToDictionary(text: string) {
return new SimpleJson.Reader(text).ToDictionary();
public static TextToDictionary(
text: string,
aggressiveFloatParsing: boolean = false
) {
return new SimpleJson.Reader(text, aggressiveFloatParsing).ToDictionary();
}

public static TextToArray(text: string) {
return new SimpleJson.Reader(text).ToArray();
public static TextToArray(
text: string,
aggressiveFloatParsing: boolean = false
) {
return new SimpleJson.Reader(text, aggressiveFloatParsing).ToArray();
}
}

export namespace SimpleJson {
export class Reader {
constructor(text: string) {
this._rootObject = JSON.parse(text);
constructor(text: string, aggressiveFloatParsing: boolean = false) {
if (aggressiveFloatParsing) {
// When the aggressiveFloatParsing argument is true, we aggressively replace
// all floats of the form "123.0" to "123.0f" so that they are recognized
// as such and converted to FloatValue later
const jsonWithExplicitFloat = text.replace(
/(,\s*)([0-9]+\.[0]+)([,]*)/g,
'$1"$2f"$3'
);
this._rootObject = JSON.parse(jsonWithExplicitFloat);
} else {
this._rootObject = JSON.parse(text);
}
}

public ToDictionary() {
Expand Down
16 changes: 15 additions & 1 deletion src/engine/Story.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ if (!Number.isInteger) {
};
}

interface JSONOptions {
aggressiveFloatParsing: boolean;
}

export class Story extends InkObject {
public static inkVersionCurrent = 21;

Expand Down Expand Up @@ -145,6 +149,7 @@ export class Story extends InkObject {
}

constructor(contentContainer: Container, lists: ListDefinition[] | null);
constructor(jsonString: string, options: JSONOptions | undefined);
constructor(jsonString: string);
constructor(json: Record<string, any>);
constructor() {
Expand All @@ -168,7 +173,16 @@ export class Story extends InkObject {
} else {
if (typeof arguments[0] === "string") {
let jsonString = arguments[0] as string;
json = SimpleJson.TextToDictionary(jsonString);
let aggressiveFloatParsing = false;

if (typeof arguments[1] != "undefined") {
let options = arguments[1] as JSONOptions | null;
({ aggressiveFloatParsing } = options || {
aggressiveFloatParsing: false,
});
}

json = SimpleJson.TextToDictionary(jsonString, aggressiveFloatParsing);
} else {
json = arguments[0] as Record<string, any>;
}
Expand Down
7 changes: 5 additions & 2 deletions src/tests/specs/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,14 @@ export class TestContext {
export function fromJsonTestContext(
name: string,
category: string,
testingErrors: boolean = false
testingErrors: boolean = false,
aggressiveFloatParsing: boolean = false
) {
let context = new TestContext(testingErrors);
let jsonContent = loadJSONFile(name, category);
context.story = new Story(jsonContent);
context.story = new Story(jsonContent, {
aggressiveFloatParsing: aggressiveFloatParsing,
});
context.bytecode = context.story.ToJson();

return context;
Expand Down
24 changes: 23 additions & 1 deletion src/tests/specs/ink/Evaluation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,35 @@ describe("Evaluation", () => {
});

// TestArithmetic
it("tests arithmetic", () => {
it("tests arithmetic with compilation", () => {
compileStory("arithmetic");
expect(context.story.ContinueMaximally()).toBe(
"36\n2\n3\n2\n2.3333333333333335\n8\n8\n"
);
});

it("tests arithmetic with default loadStory", () => {
context = testsUtils.fromJsonTestContext(
"arithmetic",
"evaluation",
false,
false
);
expect(context.story.ContinueMaximally()).toBe("36\n2\n3\n2\n2\n8\n8\n");
});

it("tests arithmetic with aggressive parse float", () => {
context = testsUtils.fromJsonTestContext(
"arithmetic",
"evaluation",
false,
true
);
expect(context.story.ContinueMaximally()).toBe(
"36\n2\n3\n2\n2.3333333333333335\n8\n8\n"
);
});

// TestBasicStringLiterals
it("tests basic string literal", () => {
compileStory("basic_string_literals");
Expand Down
13 changes: 13 additions & 0 deletions src/tests/specs/inkjs/utils/SimpleJson.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,4 +218,17 @@ describe("SimpleJson.Reader", () => {
SimpleJson.TextToDictionary(jsonString);
}).toThrow();
});

it("parses a JSON object string with agressive float parsing", () => {
let jsonString = '{"key":"value", "array": [1, 2, null, 3.0, false]}';
let object = {
array: [1, 2, null, "3.0f", false],
key: "value",
};

let reader = new SimpleJson.Reader(jsonString, true);

expect(reader.ToDictionary()).toEqual(object);
expect(SimpleJson.TextToDictionary(jsonString, true)).toEqual(object);
});
});

0 comments on commit 968b236

Please sign in to comment.