DavidKinder / Inform6

The latest version of the Inform 6 compiler, used for generating interactive fiction games.
http://inform-fiction.org/
Other
204 stars 35 forks source link

Feature: Code annotations #189

Open erkyrath opened 2 years ago

erkyrath commented 2 years ago

This is a "would make life easier for Inter" suggestion from Graham.

Inter sometimes wants to place additional annotations on I6 source code in kits and Replace directives. For example:

+replacing
Constant MAGIC = 8576;

+replacing(from BasicInformKit)
[ BlkValueFree; ... ];

(The + replacing lines are the new annotations. This is Graham's original syntax suggestion -- other possibilities below. Each annotation applies to the following top-level declaration, whether that's a Constant or Property or Object or function or whatever.)

These annotations would not be passed through to auto.inf and the actual I6-to-Glulx compilation stage. They would only be used in the I6-to-Inter stage. However, it is desirable to account for them in the I6 language definition. The classic I6 compiler should be able to skip over them with a warning/error and compile the rest of the source code.

Other uses

We already have a sort of annotation syntax in the language: the compiler option header comment.

!% -s
!% OMIT_UNUSED_ROUTINES=1

Maybe the new annotations should be unified with this.

We've also considered the idea of opt-in type annotations (https://github.com/DavidKinder/Inform6/issues/170). I haven't come up with a syntax for this, but maybe it fits in the same bucket.

(Global, Array, Property, and so on are natural fits for a per-declaration annotation syntax:

+type(String)
Global namestr;

It doesn't really work for function local variables though.)

Possible syntax:

I see roughly three ways to spell this idea.

A special lexer token

This was Graham's original suggestion. If you see + at the top level, parse out an identifier followed by an optional parenthesized expression (balancing parens, but not otherwise parsing the contents). Treat this as a new lexer token type.

(Friendly amendment: I might suggest ##replacing() instead of +replacing(). Looks less like an expression or function call left lying around loose.)

This is pretty messy for a lexer token. Inter would need to do its own parsing of the contents. So would the classic I6 compiler, if it developed uses for annotations.

Magic comments

!+replacing(from Whatever)
Constant MAGIC = 8576;

We already have magic comments. On the down side, it's language abuse. Also comments are line-oriented, unlike (almost) everything else in the language.

It would be hard to make the classic I6 compiler recognize magic comments. (The current use of header comments uses an ad-hoc parser which is completely separate from the "real" lexer system. This is already dangerous.)

Same problem as above re unparsed comments: both Inter and I6 would need special parsing.

New directive

Annotation replacing(from Whatever);
Constant MAGIC = 8576;

The Annotation directive is followed by a sequence of lexer tokens, following the pattern described above. (A symbol, then optionally a parenthesized expression.) This lets us leverage the existing lexer.

The down side: it's a directive which affects following directives in a possibly surprising way. It also couldn't be used to support the existing magic header comments; those are parsed before the lexer is available.

It also ties us to the existing syntax of directives. For example, directives are case-insensitive and permit an optional '#' sign at the front. So #ANNOtation would necessarily be a valid way to write this.

Do nothing

Leave annotations out of the I6 language definition and the classic compiler. Inter can generate them (with whatever syntax it likes) and then consume them without the classic compiler ever noticing.

This would work, but it feels like a step towards forking the I6 language into two dialects.

Other questions

If the I6 compiler (or Inter for that matter) runs into an unrecognized annotation, what should it do? Graham originally suggested a warning, but DavidK argued that it should be a compilation error. I think I agree with David.

(This is all reminiscent of the #pragma debate in C. Pragmas were introduced as a way to add compiler-specific behavior; you were supposed to ignore unrecognized pragmas. But I think people generally now consider that policy a mistake.)

Can you stick an annotation in an #ifdef or #include? It's hard to make the regular compiler reject such things. It's designed to treat syntax in an #include exactly the same as if it were inline.

Kroc commented 2 years ago

Another possibility is Rust's attribute syntax: "#[attribute]" https://doc.rust-lang.org/rust-by-example/attribute.html

erkyrath commented 8 months ago

As of Feb 2024:

Code annotations are now actively used in the I6-to-Inter compiler, and it seems to me that they might be useful for I6 purposes unrelated to I7. On the other hand, I7 has nothing it positively needs to annotate in the I6 code it is generating itself. So the status quo isn't holding things up, and I can't say it's in any way urgent.