Inform 7 v10.1.0 is now open-source

figured out what was the issue, i was building it with the package creator for the package manager of the OS i am using. it seems the problem was that the package creator adds some default flags to the build process cause the tools to not be created correctly so the tools were not able to build the kits even though they worked well enough to seem to work. i was able to add some variables to disable these default flags, which after building seems to build the tools correctly and the kits correctly now.

Also, just to check—is this an actual (report-filing-worthy) bug in inweb? Numbers in comments seem to disappear.

I’m assuming those comments are supposed to say “1st”, “2nd”, “3rd”, and so on.

EDIT: Never mind, I’m pretty sure this is a bug, because it apparently loses the first character of every comment. Now to go figure out how to report it.

I reported that one a couple days ago, Daniel: I7-2145

1 Like

It would seem entirely logical from a software development perspective to treat I6/z-Machine as a branch and separate release of Inform 10+ and have the main branch be pure Glulx. Dropping down to C would seem to be a healthy way to handle special cases. they really are different “domains” of IF implementation and keeping them “married” might muddle things. But I’m just thinking out loud. I’d be of no help on any of the work you people are doing.

On Amanda’s question, whose irony can be parsed as “hitting the intrinsic limits of NL programming languages”, I suspect that we have already a sizeable bug report repository in the form of this very category (authoring/inform 7), many (solved) question are of the “how to render this or that in the NL of Inform 7 ?” type, and if Graham find the time for browsing these questions, I’m sure that the road for significant I7 NL improvement is opened…

Best regards from Italy,
dott. Piergiorgio.

So way back I talked about v10 rejecting page turned to by, which had been accepted by 9.3

A page is a kind of object.
Page-turning relates various pages to various pages.
The verb to turn to (he turns to, they turn to, he turned to, it is turned to, it is turning to) implies the page-turning relation.

[... then, within a phrase... ]

repeat with p running through pages turned to by page1:

So it turns out that given Fubar relates one thing to various things. both 9.3/6M62 and 10.1 will accept all of these verb definitions:

The verb to barate implies the fubar relation.
The verb to be carated implies the reversed fubar relation.
The verb to be darated by implies the reversed fubar relation.
The verb to be garated to by implies the reversed fubar relation.
The verb to carry to (he carries to, they carry to, he carried to, it is carried to, it is carrying to) implies the fubar relation.

and for both To verb to farate to (he farates to, they farate to, he farated to, it is farated to, it is farating to) implies the fubar relation. will fail compilation.

But despite accepting the verb definition, neither will compile:

repeat with c running through things carated by the player begin;
    say "carated [c].";
  end repeat;
 repeat with d running through things darated to by the player begin;
    say "darated [d].";
  end repeat;

In both, these work:

repeat with b running through things barated by the player begin;
    say "barated [b].";
  end repeat;
repeat with g running through things garated to by the player begin;
    say "garated [g].";
  end repeat;

But this works only in 6M62 and fails to compile in v10:

repeat with ct running through things carried to by the player begin;
    say "carried to [ct].";
  end repeat;

Anyway, this works in v10:

The verb to be turned to by implies the reversed page-turning relation.
[...]
repeat with p running through pages turned to by page1 begin;
    say "turn to by: [p].";
  end repeat;

I got bored, so I decided to look into compiling Inform with the Visual C++ command line tools (and aspirationally Visual Studio, though without specific support for literate programming it doesn’t add much value over command line).

The makefiles assume that if you’re building on Windows you’re using Clang and you’re in a POSIX environment, neither of which were true in my case. I made a .proj that spat out a bunch of .vcxprojs that know how to tangle before compiling and linking, but otherwise were vanilla Visual Studio projects with the appropriate flags (/D PLATFORM_WINDOWS=1 /D _WIN32_WINNT=0x0600 /std:c11). I did not migrate over indulgence levels, weaving, testing, or any other build targets. Almost all the problems I ran into were in Foundation, but there were two in Inform.

Foundation, Chapter 1, Windows Platform:

@@ -19,1 +19,1 @@ (very early code)
 #include <dirent.h>

The Windows SDK doesn’t come with a dirent.h, so I downloaded a polyfill by Toni Ronkko.

Foundation, Chapter 2, Streams:

@@ -49,3 +49,3 @@
 same, if you're porting this code, you may need to rewrite the macro with
 |...| in place of |args...| in the header, and then |__VA_ARGS__| in place
 of |args| in the definition: that being the modern way, apparently.

Visual C++ only supports the modern way, so I was very thankful to find this as it explained exactly what needed to be done not only here but several other places as well.

@@ -58,12 +58,12 @@
 = (early code)
-#define WRITE(args...) Writers::printf(OUT, args)
+#define WRITE(...) Writers::printf(OUT, __VA_ARGS__)

-#define PRINT(args...) Writers::printf(STDOUT, args)
+#define PRINT(...) Writers::printf(STDOUT, __VA_ARGS__)

-#define WRITE_TO(stream, args...) Writers::printf(stream, args)
+#define WRITE_TO(stream, ...) Writers::printf(stream, __VA_ARGS__)

-#define LOG(args...) Writers::printf(DL, args)
+#define LOG(...) Writers::printf(DL, __VA_ARGS__)

-#define LOGIF(aspect, args...) { \
-       if (Log::aspect_switched_on(aspect##_DA)) Writers::printf(DL, args); \
-}
+#define LOGIF(aspect, ...) { \
+       if (Log::aspect_switched_on(aspect##_DA)) Writers::printf(DL, __VA_ARGS__); \
+}

Here, Visual C++ complains about an unknown size. The other compilers assume bytes, but VC++ needs to be told that explicitly for safety:

@@ -779,7 +779,7 @@ <Ensure there is room to expand the escape sequence into>
                        int needed = offset + ((int) sizeof(text_stream)) + 32;
                        void *further_allocation = Memory::malloc(needed, STREAM_MREASON);
                        if (further_allocation == NULL) Errors::fatal("Out of memory");
-                       text_stream *continuation = (text_stream *) (further_allocation + offset);
+                       text_stream *continuation = (text_stream *) ((char *)further_allocation + offset);
                        Streams::initialise(continuation, FOR_CO_STRF);
                        continuation->write_to_memory = further_allocation;
                        continuation->chars_capacity = 2*stream->chars_capacity;

Foundation, Chapter 2, Methods:
Again, variadic macros have a specific required syntax for VC++:

@@ -49,10 +49,10 @@
 What these do is to use typedef to give the name |X_type| to the type of all
 functions sharing the method ID |X|.

-@d INT_METHOD_TYPE(id, args...)
-       typedef int (*id##_type)(args);
-@d VOID_METHOD_TYPE(id, args...)
-       typedef void (*id##_type)(args);
+@d INT_METHOD_TYPE(id, ...)
+       typedef int (*id##_type)(__VA_ARGS__);
+@d VOID_METHOD_TYPE(id, ...)
+       typedef void (*id##_type)(__VA_ARGS__);

Similar problems and solutions are at the method call macros on lines 122/126 and 144/147, not shown here.

Foundation, Chapter 3, Case-Insensitive Filenames:
While strcasecmp is a thing in the POSIX standard, for some reason Microsoft compilers prefer stricmp.

@@ -230,7 +230,7 @@ int CIFilingSystem::match_in_directory(void *vd, char *name, char *last_match)

        last_match[0] = 0;
        while ((dirp = readdir(d)) != NULL) {
-               if (strcasecmp(name, dirp->d_name) == 0) {
+               if (stricmp(name, dirp->d_name) == 0) {
                        rc++;
                        strcpy(last_match, dirp->d_name);
                }

Foundation, Chapter 5, HTML:

@@ -129,21 +129,21 @@
 @ We will open and close all HTML tags using the following macros, two
 of which are variadic and have to be written out the old-fashioned way:
 
 @d HTML_TAG(tag) HTML::tag(OUT, tag, NULL);
 @d HTML_OPEN(tag) HTML::open(OUT, tag, NULL, __FILE__, __LINE__);
 @d HTML_CLOSE(tag) HTML::close(OUT, tag, __FILE__, __LINE__);

 =
-#define HTML_TAG_WITH(tag, args...) { \
+#define HTML_TAG_WITH(tag, ...) { \
        TEMPORARY_TEXT(details) \
-       WRITE_TO(details, args); \
+       WRITE_TO(details, __VA_ARGS__); \
        HTML::tag(OUT, tag, details); \
        DISCARD_TEXT(details) \
 }

-#define HTML_OPEN_WITH(tag, args...) { \
+#define HTML_OPEN_WITH(tag, ...) { \
        TEMPORARY_TEXT(details) \
-       WRITE_TO(details, args); \
+       WRITE_TO(details, __VA_ARGS__); \
        HTML::open(OUT, tag, details, __FILE__, __LINE__); \
        DISCARD_TEXT(details) \
 }

At this point I was able to tangle and compile Inweb and Intest without issue. The linker was unhappy, though.

Foundation, Chapter 1, Windows Platform (again):

@@ -75,1 +75,1 @@ Environment variables.
 			if (SHGetFolderPathA(0, CSIDL_PERSONAL, 0, SHGFP_TYPE_CURRENT, value) == 0)

I added Shell32.lib to the linker inputs and got myself some executables. There was one warning about stuffing a 64-bit pointer into a 32-bit long, but unless special compiler options are used (mainly security-related) it’s unlikely to cause a problem in practice. I made sure Intest ran and Inweb was able to tangle and weave itself and all was as it should be.

I moved on to Inform the Repo, spent far too much time on nonsense with MSBuild inline tasks, and got to the point where I could keep building.

Inform 7, Core Module, Chapter 3, Plugin Manager:
Fourth verse, same as the first.

@@ -193,13 +193,13 @@
 We must take care that the variables introduced in the macro body do not mask
 existing variables used in the arguments; the only way to do this is to give
 them implausible names.

-@d PLUGINS_CALL(code, args...) {
+@d PLUGINS_CALL(code, ...) {
        void *R_plugin_pointer_XYZZY; /* no argument can have this name */
        LOOP_OVER_LINKED_LIST(R_plugin_pointer_XYZZY, void, plugin_rulebooks[code]) {
                int (*R_plugin_rule_ZOOGE)() = (int (*)()) R_plugin_pointer_XYZZY; /* or this one */
-               int Q_plugin_return_PLUGH = (*R_plugin_rule_ZOOGE)(args); /* or this */
+               int Q_plugin_return_PLUGH = (*R_plugin_rule_ZOOGE)(__VA_ARGS__); /* or this */
                if (Q_plugin_return_PLUGH) return Q_plugin_return_PLUGH;
        }
        return FALSE;
 }

Inform 7, Problems, Chapter 2, Problems Level 2:
In Words, Chapter 3, Wordings, the author notes that for EMPTY_WORDING GCC requires a different pronunciation than usual for static initializers. VC++ additionally requires that special pronunciation here:

@@ -341,8 +341,8 @@ Appending source.
-wording appended_source = EMPTY_WORDING;
+wording appended_source = EMPTY_WORDING_INIT;
 void Problems::append_source(wording W) {
	 appended_source = W;
 }
 void Problems::transcribe_appended_source(void) {
	 if (Wordings::nonempty(appended_source))
		 ProblemBuffer::copy_source_reference(appended_source);
 }

With that, I was able to build everything Inform’s makefile defined as a “tool” as well, producing several thousand warnings about VC++ not understanding Clang’s warning suppression pragmas as well as executables that at the very least could print out their version number. I was also able to tangle the extensions, and convinced Inter to translate the kits, though Inbuild couldn’t find Inter to do so automatically. I’ll do further testing once there aren’t explosions directly outside my window.

Why post here instead of a GitHub pull request? Neither Dr. Nelson nor Mr. Kinder have shown any desire to support building in VC++, and these changes don’t have a positive impact on the compilers they do use. Adding support to Inweb for making MSBuild project files in addition to makefiles is a bit too aspirational, and my hand-rolled ones aren’t anywhere near ready to publish – especially without the ability to run any tests, produce the companion books or weave the documentation, or any of the other things the makefile does, not to mention being unhappy with pathnames with spaces – but all of that would need to be ready before such a request might be accepted. That said, I hope this post might help other people start playing with 10.1 if they have Visual Studio but not Clang and Make.

(Oh, and I transfer any copyright that might exist to Graham Nelson. Just in case these changes are better than I thought.)

6 Likes

It’s interesting to see that Visual Studio with Clang doesn’t require that many changes to get Inform to compile, I had wondered. The test harness will be more of a problem, though, as that really does rely on the presence of a Unix-y shell environment.

1 Like

I do disagree. I want to retain the ability to target the Z-machine until I run out of space. Extensions are generally quite compact – optimized in a way games typically aren’t – and I think it’s quite possible to make a small game targeting the Z-machine which uses a bunch of seemingly-complicated (but actually quite small, when compiled) extensions. After digging into the innards for a while, I don’t see much which varies between the two. I’d probably just build a Z-machine malloc. (Though for the thing I’m trying to implement right now, I actually just want a data stack: alloca.)

As has happened repeatedly before with Inform, the major problem users are facing is bad documentation: it’s not documented what’s supported and what isn’t. It never has been. “Literate programming” apparently didn’t help much with this. You can get away with very quirky behavior if it’s well-documented. (I still think Inform 7 needs a real reference manual, which it has never had. With the code open-sourced, it’s now possible for a third party to write it.)

Individual property declarations are highly useful and should be supported in Inter. “Property individual propname;” should be sufficient.

P.S. I’ve been working on a different programming project for the last month. I’m back for now.

4 Likes

I’m with Nathaniel on this one in many ways, but I want to lay out my full experience.

I like keeping games small when I can, and I also like having the programming exercise of keeping the blorb as small as possible for as long as possible. This isn’t just code-golf or whatever–it helps test runs go faster. I find my writing is divided pretty evenly between Z8 and Glulx. And yet I include a few headers per game – mostly of stubs, etc. But they are still headers. Stuff like my own stubs, or Undo Output Control.

On the other hand it looks like, with all the bells and whistles Inform has, this may not be practical. (I use 6G, and I can see building the same story.ni for later versions takes up progressively more disk space.) But I think for small-game jams, it’d be nice to keep the z-machine if possible. To me it encourages efficient coding and game design.

Yet I also have seen how projects independent of cutting-edge Inform, such as PunyInform, do the same thing, and since Dialog is maturing as a language but (IIRC) allows for lightweight builds where you can include extensions, thi may be something worth deprecating to allow the full features you want to present.

I can keep using 6G for z-machine projects until the cows come home. But I’d be sad to see a z-machine setting go, for Inform.

Now that Inform is open source, I’m interested in browsing to see how, after the player enters a command, the framework is called and the world model is updated.

But all the InWeb-generated documentation I’ve seen seems to be about the toolchain that compiles an Inform 7 story.

Where should I start looking?

1 Like

Sounds like you want to look at what’s now labeled as the if module.

1 Like

Or possible you are looking for the Action-processing rulebook in the Standard Rules:

Variables and Rulebooks

The basic game cycle is defined by the turn sequence rules, which begin with the “parse command rule” and the “generate action rule”. The core of the latter is the Action-processing rulebook.

1 Like

Note that the “if module”, which Mike Russo linked to, is part of the Inform 7 compiler.

If you’re interested in what happens during gameplay, you want the Standard Rules (I7 code) and the various Kits (I6 code).

Very true - I thought from the “now that Inform is open source” bit that he was looking for something other than the standard rules, but rereading the question that might well be wrong.

Good. Thank you.

This is the feature that interests me the most. Has the code been written in a way that would allow additional target languages to be added?

For example, could others create extensions to output Python, BBC BASIC, JavaScript etc. without needing to change the main source?

2 Likes

Yes, the new Inform7 code produces a “neutral”/abstract intermediate representation (IR) called Inter that can be compiled into or interpreted by many different frontends. See Using Inter for details.

3 Likes

Whose is the same frontend/backend technique used by the GCC compiler suite.

Inter can open new crosscompiling possibilities between IF languages and story file formats, for example, generating a glulx story file from TADS 3 or Alan 3 source, or generating TADS2/3 story file from Hugo or even Inform 6/7; of course, one can write front-end for general-purpose languages, esp. FORTRAN…

Best regards from Italy,
dott. Piergiorgio.

1 Like

I did wonder whether it would also be possible to pass other IF languages through Inter, but I would imagine that they have less in common with each other than general programming languages do.

From what little I have read about the new Inform system, there are five stages of compilation before Inter, which would need to be rewritten for whatever other IF language was used.

1 Like