Skip to content

Commit

Permalink
Merge pull request #986 from y-lohse/v1.1
Browse files Browse the repository at this point in the history
ink v1.1 ~ inkjs v2.2
  • Loading branch information
smwhr committed Oct 21, 2022
2 parents 1a79adf + 53e806f commit 04edb26
Show file tree
Hide file tree
Showing 40 changed files with 579 additions and 157 deletions.
37 changes: 19 additions & 18 deletions README.md
Expand Up @@ -155,21 +155,22 @@ See [Differences with the C# Compiler](docs/compiler-differences.md).

## Compatibility table

| _inklecate_ version | _inkjs_ version |
| :-----------------: | :-------------: |
| 0.3.5 – 0.4.0 | 1.0.0 – 1.1.0 |
| 0.4.1 – 0.5.0 | 1.1.1 – 1.1.3 |
| 0.5.1 | 1.2.0 |
| 0.6.0 | 1.3.0 |
| 0.6.1 | 1.4.0 – 1.4.1 |
| 0.6.2 | 1.4.2 |
| 0.6.3 | 1.4.3 |
| 0.6.4 | 1.4.4 – 1.4.6 |
| 0.7.0 | 1.5.0 – 1.5.1 |
| 0.7.1 | 1.5.2 |
| 0.7.2 – 0.7.4 | 1.6.0 |
| 0.8.0 – 0.8.1 | 1.7.1 – 1.7.2 |
| 0.8.2 | 1.8.0 – 1.9.0 |
| 0.8.3 | 1.10.0 – 1.10.5 |
| 0.9.0 | 1.11.0 |
| 1.0.0 | 2.0.0 |
| _inklecate_ version | _inkjs_ version | _json_ version |
| :-----------------: | :-------------: | :------------: |
| 0.3.5 – 0.4.0 | 1.0.0 – 1.1.0 | 18 |
| 0.4.1 – 0.5.0 | 1.1.1 – 1.1.3 | |
| 0.5.1 | 1.2.0 | |
| 0.6.0 | 1.3.0 | |
| 0.6.1 | 1.4.0 – 1.4.1 | |
| 0.6.2 | 1.4.2 | |
| 0.6.3 | 1.4.3 | |
| 0.6.4 | 1.4.4 – 1.4.6 | |
| 0.7.0 | 1.5.0 – 1.5.1 | |
| 0.7.1 | 1.5.2 | |
| 0.7.2 – 0.7.4 | 1.6.0 | |
| 0.8.0 – 0.8.1 | 1.7.1 – 1.7.2 | |
| 0.8.2 | 1.8.0 – 1.9.0 | |
| 0.8.3 | 1.10.0 – 1.10.5 | |
| 0.9.0 | 1.11.0 | 19 |
| 1.0.0 | 2.0.0 - 2.1.0 | 20 |
| 1.1.1 | 2.2.0 | 21 |
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "inkjs",
"version": "2.1.0",
"version": "2.2.0",
"description": "A javascript port of inkle's ink scripting language (http://www.inklestudios.com/ink/)",
"main": "dist/ink-full.js",
"types": "ink.d.ts",
Expand Down
44 changes: 37 additions & 7 deletions script/inkjs-compiler.ts
Expand Up @@ -55,10 +55,19 @@ if(jsonStory && write){
}

if(jsonStory && play){
const prompt = readline.createInterface({
const rl = readline.createInterface({
input: process.stdin, //or fileStream
output: process.stdout
});

const prompt = () => {
return new Promise<string>((resolve, reject) => {
rl.question('?> ', (answer: string) => {
resolve(answer)
})
})
}

const play = async () =>{
const story = new Story(jsonStory);

Expand All @@ -78,18 +87,39 @@ if(jsonStory && play){

for (let i=0; i<story.currentChoices.length; ++i) {
const choice = story.currentChoices[i];
process.stdout.write( `${i+1}: ${choice.text}\n` );
process.stdout.write( `${i+1}: ${choice.text}` );
if( story.currentChoices[i].tags !== null
&& story.currentChoices[i].tags!.length > 0){
process.stdout.write( " # tags: " + story.currentChoices[i].tags!.join(", ") );
}
process.stdout.write("\n")
}
process.stdout.write("?> ");
for await (const line of prompt) {
const choiceIndex = parseInt(line) - 1;
story.ChooseChoiceIndex(choiceIndex);
}
do{
const answer: string = await prompt();
if(answer.startsWith("->")){
const target = answer.slice(2).trim()
try{
story.ChoosePathString(target)
break;
}catch(e){
process.stdout.write(e.message + '\n');
}
}else{
const choiceIndex = parseInt(answer) - 1;
try{
story.ChooseChoiceIndex(choiceIndex);
break;
}catch(e){
process.stdout.write(e.message + '\n');
}
}
}while(true);
}while(true);

}
play().then(()=>{
process.stdout.write("\nDONE.")
process.stdout.write("DONE.\n")
process.exit(0);
});

Expand Down
1 change: 1 addition & 0 deletions src/compiler/Parser/CustomFlags.ts
@@ -1,3 +1,4 @@
export enum CustomFlags {
ParsingString = 0x1,
TagActive = 0x2,
}
142 changes: 85 additions & 57 deletions src/compiler/Parser/InkParser.ts
Expand Up @@ -48,7 +48,6 @@ import {
} from "./StringParser/StringParser";
import { StringParserElement } from "./StringParser/StringParserElement";
import { Tag } from "./ParsedHierarchy/Tag";
import { Tag as RuntimeTag } from "../../engine/Tag";
import { Text } from "./ParsedHierarchy/Text";
import { TunnelOnwards } from "./ParsedHierarchy/TunnelOnwards";
import { VariableAssignment } from "./ParsedHierarchy/Variable/VariableAssignment";
Expand Down Expand Up @@ -227,6 +226,14 @@ export class InkParser extends StringParser {
this.SetFlag(Number(CustomFlags.ParsingString), value);
}

get tagActive(): boolean {
return this.GetFlag(Number(CustomFlags.TagActive));
}

set tagActive(value: boolean) {
this.SetFlag(Number(CustomFlags.TagActive), value);
}

public readonly OnStringParserError = (
message: string,
index: number,
Expand Down Expand Up @@ -441,6 +448,8 @@ export class InkParser extends StringParser {
// * "Hello[."]," he said.
const hasWeaveStyleInlineBrackets: boolean = this.ParseString("[") !== null;
if (hasWeaveStyleInlineBrackets) {
this.EndTagIfNecessary(startContent);

const optionOnlyTextAndLogic = this.Parse(
this.MixedTextAndLogic
) as ParsedObject[];
Expand All @@ -451,6 +460,8 @@ export class InkParser extends StringParser {

this.Expect(this.String("]"), "closing ']' for weave-style option");

this.EndTagIfNecessary(optionOnlyContent);

let innerTextAndLogic = this.Parse(
this.MixedTextAndLogic
) as ParsedObject[];
Expand All @@ -461,6 +472,8 @@ export class InkParser extends StringParser {

this.Whitespace();

this.EndTagIfNecessary(innerContent ?? startContent);

// Finally, now we know we're at the end of the main choice body, parse
// any diverts separately.
const diverts: ParsedObject[] = this.Parse(
Expand Down Expand Up @@ -492,10 +505,7 @@ export class InkParser extends StringParser {
innerContent = new ContentList();
}

const tags = this.Parse(this.Tags) as ParsedObject[];
if (tags !== null) {
innerContent.AddContent(tags);
}
this.EndTagIfNecessary(innerContent);

// Normal diverts on the end of a choice - simply add to the normal content
if (diverts !== null) {
Expand Down Expand Up @@ -1011,20 +1021,6 @@ export class InkParser extends StringParser {
this.MixedTextAndLogic
) as ParsedObject[];

// Terminating tag
let onlyTags: boolean = false;
const tags = this.Parse(this.Tags) as ParsedObject[];
if (tags) {
if (!result) {
result = tags;
onlyTags = true;
} else {
for (const tag of tags) {
result.push(tag);
}
}
}

if (!result || !result.length) {
return null;
}
Expand All @@ -1046,9 +1042,16 @@ export class InkParser extends StringParser {
this.TrimEndWhitespace(result, false);
}

// Add newline since it's the end of the line
// (so long as it's a line with only tags)
if (!onlyTags) {
this.EndTagIfNecessary(result);

// If the line doens't actually contain any normal text content
// but is in fact entirely a tag, then let's not append
// a newline, since we want the tag (or tags) to be associated
// with the line below rather than being completely independent.
let lineIsPureTag =
result.length > 0 && result[0] instanceof Tag && result[0].isStart;

if (!lineIsPureTag) {
result.push(new Text("\n"));
}

Expand All @@ -1068,7 +1071,7 @@ export class InkParser extends StringParser {
// Either, or both interleaved
let results: ParsedObject[] = this.Interleave<ParsedObject>(
this.Optional(this.ContentText),
this.Optional(this.InlineLogicOrGlue)
this.Optional(this.InlineLogicOrGlueOrStartTag)
);

// Terminating divert?
Expand All @@ -1084,6 +1087,9 @@ export class InkParser extends StringParser {
results = [];
}

// End previously active tag if necessary
this.EndTagIfNecessary(results);

this.TrimEndWhitespace(results, true);

results.push(...diverts);
Expand Down Expand Up @@ -1224,6 +1230,8 @@ export class InkParser extends StringParser {

diverts = [];

this.EndTagIfNecessary(diverts);

// Possible patterns:
// -> -- explicit gather
// ->-> -- tunnel onwards
Expand Down Expand Up @@ -2616,8 +2624,8 @@ export class InkParser extends StringParser {
return result;
};

public readonly InlineLogicOrGlue = (): ParsedObject =>
this.OneOf([this.InlineLogic, this.Glue]) as ParsedObject;
public readonly InlineLogicOrGlueOrStartTag = (): ParsedObject =>
this.OneOf([this.InlineLogic, this.Glue, this.StartTag]) as ParsedObject;

public readonly Glue = (): Glue | null => {
// Don't want to parse whitespace, since it might be important
Expand All @@ -2635,6 +2643,9 @@ export class InkParser extends StringParser {
return null;
}

let wasParsingString = this.parsingStringExpression;
let wasTagActive = this.tagActive;

this.Whitespace();

const logic = this.Expect(
Expand All @@ -2643,6 +2654,7 @@ export class InkParser extends StringParser {
) as ParsedObject;

if (logic === null) {
this.parsingStringExpression = wasParsingString;
return null;
}

Expand All @@ -2657,6 +2669,19 @@ export class InkParser extends StringParser {

this.Expect(this.String("}"), "closing brace '}' for inline logic");

// Allow nested strings and logic
this.parsingStringExpression = wasParsingString;

// Difference between:
//
// 1) A thing # {image}.jpg
// 2) A {red #red|blue #blue} sequence.
//
// When logic ends in (1) we still want tag to continue.
// When logic ends in (2) we want to auto-end the tag.
// Side note: we simply disallow tags within strings.
if (!wasTagActive) this.EndTagIfNecessary(contentList);

return contentList;
};

Expand Down Expand Up @@ -2710,6 +2735,8 @@ export class InkParser extends StringParser {
this.InnerExpression,
];

let wasTagActiveAtStartOfScope = this.tagActive;

// Adapted from "OneOf" structuring rule except that in
// order for the rule to succeed, it has to maximally
// cover the entire string within the { }. Used to
Expand Down Expand Up @@ -3204,50 +3231,51 @@ export class InkParser extends StringParser {
* Begin Tags section.
*/

private _endOfTagCharSet: CharacterSet = new CharacterSet("#\n\r\\");

public readonly Tag = (): Tag | null => {
public readonly StartTag = (): ParsedObject | null => {
this.Whitespace();

if (this.ParseString("#") === null) {
return null;
}

this.Whitespace();

let sb = "";
do {
// Read up to another #, end of input or newline
const tagText: string =
this.ParseUntilCharactersFromCharSet(this._endOfTagCharSet) || "";
sb += tagText;

// Escape character
if (this.ParseString("\\") !== null) {
const c: string = this.ParseSingleCharacter();
if (c !== "\0") {
sb += c;
}

continue;
}
if (this.parsingStringExpression) {
this.Error(
"Tags aren't allowed inside of strings. Please use \\# if you want a hash symbol."
);
}

break;
} while (true);
let result: ParsedObject | null = null;
if (this.tagActive) {
let contentList = new ContentList();
contentList.AddContent(new Tag(/*isStart:*/ false));
contentList.AddContent(new Tag(/*isStart:*/ true));
result = contentList;
} else {
result = new Tag(/*isStart:*/ true);
}
this.tagActive = true;

const fullTagText = sb.trim();
this.Whitespace();

return new Tag(new RuntimeTag(fullTagText));
return result;
};

public readonly Tags = (): Tag[] | null => {
const tags = this.OneOrMore(this.Tag) as Tag[];
if (tags === null) {
return null;
public EndTagIfNecessary(outputContentList: ParsedObject[] | null): void;
public EndTagIfNecessary(outputContentList: ContentList | null): void;
public EndTagIfNecessary(
outputContentList: ParsedObject[] | ContentList | null
): void {
if (this.tagActive) {
if (outputContentList != null) {
if (outputContentList instanceof ContentList) {
outputContentList.AddContent(new Tag(/*isStart:*/ false));
} else {
outputContentList.push(new Tag(/*isStart:*/ false));
}
}
this.tagActive = false;
}

return tags;
};
}

/**
* End Tags section.
Expand Down

0 comments on commit 04edb26

Please sign in to comment.