Code Monkey home page Code Monkey logo

alan's Introduction

Jenkins Status  Travis CI Status  Coverage Status  License

Alan — the Adventure Language

Alan is a language for developing and running interactive fiction, a.k.a adventure games, i.e. the games are text-based, in the best of worlds reads like interactive literature, and gives you "the best graphics device available — the human brain".

What is this?

This repository is part of the larger ALAN-IF set of repositories with tools, examples and documentation for the Alan system.

This particular repository contains the source code and build environment for the Alan compiler, interpreters and an Alan v2 to Alan v3 source converter.

How to build?

The build system is fairly self-configuring, you should be able to just clone and 'make' on the following platform:

  • Cygwin
  • Msys2, both native and 32/64-bit Windows-compatible
  • Linux, most flavours including WSL (Windows Subsystem for Linux)
  • MacOS/Darwin, both native and with Homebrew GCC

To run unittests you need Cgreen, and to run all the regression tests you need a Java runtime.

What's in the box?

From the repo you can build

  • command line compiler and interpreter for your OS
  • GLK-based interpreter, you need a GLK-library
  • Gargoyle plug-in, which can be used instead of the plug-in that comes with Gargoyle

And if your environment allows cross-compilation to Windows you can build

  • WinArun, a WindowsGLK based GUI-interpreter
  • WinAlan, a Windows "GUI" compiler (but a better choice is probably the AlanIDE)

To cross-compile (Cygwin, MSYS2 and most Linuxen can be made to do this) you have to install the cross-compilation tools (sudo apt install mingw-w64 or equivalent) and a matching iconv (sometimes a separate package like win-iconv-mingw-w64-dev).

What's missing?

A lot probably, but I wanted this Readme to exist as quickly as possible. Please, join, fork, pull request or file issues if there is something you want included here.

Useful Links

alan's People

Contributors

tajmone avatar thoni56 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

alan's Issues

Compiler BUG: Silently Fail on Malformed WHERE

@thoni56, today while experiment with some tests I added the following (wrong) code to an adventure, and it would silently fail to compile:

Syntax makewear = makewear (act) (obj)
  Where act IsA actor
    else "This verb can only be used on actors!"
  And obj IsA object
    else "You can only make actor wear object!"
  And obj is wearable
    else "$+2 is not a wearable item!"

Add to every thing
  Verb makewear
    Does
      Locate obj in act.
      Make obj puesto.
      Set portador of obj to act.
      "Done, now $+1 is wearing $+2."
  End verb.
End add to.

The problem is the line:

  And obj is wearable
    else "$+2 is not a wearable item!"

which should have been a VERB CHECK, not part of the WHERE construct.

But somehow the ALAN compiler is unable to report this error, and simply fails silently, without any message at all.

Feature Request: libARun

@thoni56,

it would be quite useful if the Alan project could also include a library version of the ARun interpreter (static or dynamic) that could be bound to third party applications in order to run automated game sessions.

It could be used, for example, to create a tool similar to the Inform 7 Skein, which would allow to create a graph of different possible commands to be fed to libARun in order to efficiently simulate/test adventure sessions.

Also, it would be useful for big projects (like libraries) that rely on a test suite to check that changes to code base don't introduce errors. In this context, a library would speed up all tests by circumventing the need to access an external interpreter, especially when the same adventure needs to be tested multiple times with different commands scripts (for the story file would only need to be loaded once by libARun), or in case one needs a test suite that relies on pseudo-randomly generated commands to test for edge cases and uncovered error responses.

Last but not least, this would be an invaluable tool for creating a dedicated interpreter for the visually impaired, allowing easy interfacing to accessibility devices (Braille reader, speech synthesizers, voice-recognition tools, etc.). I think that accessibility is an important feature to invest in, especially since IF is one of the very few (if not the only) genre of video games accessible to the blind.

The library would need to expose some functions to:

  • load a story file into memory.
  • initiate a game session.
  • send a command to the virtual interpreter and capture the response.
  • terminate a game session.
  • free a story file from memory.
  • access debug info (optional but useful).

Since no multimedia or styles need to be supported, and all I/O operations would be in plain strings, this library wouldn't need Glk support either.

How complex would it be to add a library like this to the ALAN builds?

I wouldn't mind trying to work on it myself, but I might need some help from you for this. Also, I think that this feature would require introducing some compiler directives to the ARun sources in order to work (#ifdefs, etc.) — so chances are that to be a maintainable project it requires to be officially endorsed in the ALAN repository.

What's your view on this?

MacOSX SDKs: Odd "regression/" Folder

@thoni56, I've noticed something unusual in the Alan SDK for MacOSX , i.e. that the "saviour" adventure is being stored in a folder named regression. Doesn't really sound like an intentional folder name.

The contents of the MacOSX Beta7 SDK (alan3.0beta7.macos.tgz):

├───/games/
│   └───adventv3
│           adventV3.alan
│
├───/regression/
|       logo.png
|       saviour.alan
│
│   alan
│   alan.readme
│   alan.readme.macosx
│   arun
│   CHANGES
│   COPYING

BUG: Block Comment Hide Source Lines from Parser

ALAN 3.0beta8 build 2209 | Win 10 x64

It looks like block comments introduced another bug (this one might be quite serious, as it compromises correct source parsing).

Take the kitchen-ascii.alan sample adventure available at:

If you edit the source file, and remove the empty line between the end of the block comment and the Syntax 'look' statement:

//// Kitchen ASCII /////////////////////////////////////////////////////////////
A test adventure containing only ASCII characters.
Written by Tristano Ajmone, 2021, using ALAN 3.0beta8 build 2209.
Released into the public domain via the Unlicense: https://unlicense.org
////////////////////////////////////////////////////////////////////////////////
Syntax 'look' = 'look'.

Verb 'look'
  Does look.
End verb.

Compiling will issue the following warning:

line 8(6): 230 I : No syntax defined for this global verb, automatically used 'look'.

which of course is incorrect, since the Syntax for look is there, but it seems that the missing empty line after the closing block comment is hiding it from the parser.

BUG: ARun w/ UTF-8 BOM Solution Files

ARun is not consuming the BOM when the -u switch is explicitly set, causing the BOM character to slip through as part of the firs player input command emitted.

See:

where the output_utf8-bom_-u.a3t transcript contains the BOM:

> <0xfeff>x table
Conversion of command input from UTF-8 failed, are you sure about the input encoding? ('Illegal byte sequence')
I don't understand.

The same problem is seen in output_utf8-bom.a3t, which is generated using the same UTF-8 BOM solution file, but without passing the -u switch to ARun; except that we don't see the encoding problem suggestion as above:

> x table
I don't know the word 'ï'.

Here the BOM in this case is seen as corrupt chars, instead as a binary entity, but that's only because the file is seen as ISO-8859-1 by my editor, whereas the previous one is detected as UTF-8. Maybe this has to do with the byte order of these chars? this might explain the difference seen with -u and without it.

BOM sanitation should be applied by ARun when auto-detecting UTF-8 solution files, as well as when passing the -u switch.

I also noticed that when passing an UTF-8 + BOM file, or using the -u switch, the generated transcript (redirected to file) will be in UTF-8, but always without BOM. Probably this makes sense, since who would need a BOM in the generated file anyhow?

But I was wondering if for consistency sake it might good to have an extra option to enforce the BOM on generated transcripts — I'm just thinking aloud here, and have no particular edge- or use-case in mind.

Improve Default English MESSAGEs

@thoni56, I've just updated the default MESSAGEs in App C.1 of The ALAN Manual to match the definition found in compiler/msg.c (master branch, since I'm assuming that mirrors the current ALAN Beta) — see alan-if/alan-docs#110.

While doing so, I noticed some default English messages that could do with some text revision improvements.

WHAT

WHAT : "I don't understand."

Maybe: "I didn't understand."

WHAT_WORD

WHAT_WORD : "I don't know what you mean by '$1'."

Maybe: "I don't understand what you mean by '$1'."

AFTER_BUT

AFTER_BUT : "You must give at least one object after '$1'."

Maybe:

  • "You must supply at least one object after '$1'."
  • "You must provide at least one object after '$1'."

WHICH_START

WHICH_START : "I don't know if you mean $+1"
WHICH_COMMA : ", $+1"
WHICH_OR : "or $+1."

Maybe: "I'm not sure if you mean $+1"

WHICH_PRONOUN_START

WHICH_PRONOUN_START : "I don't know what you mean by '$1',"
WHICH_PRONOUN_FIRST : "$+1"

I used to like the older form better, so maybe:

WHICH_PRONOUN_START : "I'm not sure whether by '$1' you mean"

SEE_START

SEE_START : "There is $01"
SEE_COMMA : ", $01"
SEE_AND : "and $01"
SEE_END : "here."

The problem with this is that it should be:

SEE_START : "There are $01"

when there are multiple things in the current local, and

SEE_START : "There is $01"

if there's only one.

I think this whole group would benefit from rephrasing it in a way that work well in both cases, e.g.:

SEE_START : "You can see $01"

EMPTY_HANDED

EMPTY_HANDED : "$+1 is empty-handed."

As @AnssiR66 pointed out elsewhere, this only really works for human actors or other creatures which have hands, but not for animals, monsters, or inanimate/mechanical actors like robots, etc.

True, authors can customize these in their adventure source, but if we could come up with a species-neutral standard description it would be better.

SAVE_OVERWRITE

SAVE_OVERWRITE : "That file already exists, overwrite (RETURN confirms) ? "

There's a subtle tautology in this phrase: the very fact that you can point to the file by saying "that file" implies its existence.

What's really being said here is: The proposed filename corresponds to an existing file, do you wish to overwrite it?

Maybe it would simpler to just say:

SAVE_OVERWRITE : "Overwrite existing file (RETURN confirms) ? "

CONTAINMENT_LOOP2

CONTAINMENT_LOOP2 : "Putting $+1 in $+2 is impossible since $+2 already is inside $+1."

This sounds really bad.

Maybe something like:

  • "How? $+2 is inside $+1!"
  • "You can't put $+1 into $+2 since the former is inside the latter!"
  • "You can't put $+1 into $+2 because $+2 is inside $+1!"

or something else that's less tortuous.

Bug 997

@thoni56, while working on some test code for the ALAN i18n project, I encountered an error that seems an internal compiler bug worth reporting.

Failing to compile alan_es/tests/meta.alan due to system error:

997 S : SYSTEM ERROR: Unexpected transitivity in
        'transitivityToString()', whr.c:52

I've created a dedicated BUG_997 branch to preserve the exact state of the repository that produced that error, so you can reproduce it.

I have no idea what caused it, but I was working on the restore_test_objs event (which wasn't doing what I expected), and the error appeared when I added the Empty tObj. statement seen below:

Event restore_test_objs
  For each tObj IsA test_obj do
    Empty tObj. --> CAUSED THE ERROR!
    Locate tObj in test_objects.
  End for.
  Schedule restore_test_objs here after 1.
End Event.

I hope it might help.

Please, once solved delete the BUG_997 branch.

Location of: Case-Sensitive

What an odd bug: the location keyword in location of does not work properly if written in uppercase. Try it:

THE room IsA LOCATION
END THE room.

Start at room.
Say LOCATION of hero. --> 'location' must be lowercase!

It won't compile if any letter of location in the last line is uppercase.

Came across while editing source casing.

Grammar railroad diagram

Looking for people using CocoR I found this project and I've done a experimental tool to convert CocoR grammars to a kind of EBNF understood by https://www.bottlecaps.de/rr/ui to generate railroad diagrams see bellow the converted and with some hand made changes of alan.atg to allow view it at https://www.bottlecaps.de/rr/ui the order of the rules could be changed to a better view of the railroad diagrams. Copy and paste the EBNF bellow on https://www.bottlecaps.de/rr/ui tab Edit Grammar then switch to the tab View Diagram.

Cheers !

//"--" lf lf  '+' cr  '+' tab
adventure ::= optionaloptions declarations start
optionaloptions ::=  | genSym0 options
genSym0 ::= "options"  | "option"
options ::= option  | options option
option ::= ID "."  | "no" ID "."  | ID ID "."  | ID Integer "."
declarations ::=  | declarations declaration
declaration ::= "import"  | prompt  | messages  | class  | instance  | rule  | synonyms  | syntax  | verb  | addition  | event
prompt ::= "prompt" statements
attributes ::= attributedefinition "."  | attributes attributedefinition "."
attributedefinition ::= ID  | "not" ID  | ID STRING  | ID ID  | ID optionalminus Integer  | ID "{" optionalmembers "}"
optionalmembers ::=  | setmembers
setmembers ::= setmember  | setmembers "," setmember
setmember ::= what  | STRING  | optionalminus Integer
synonyms ::= "synonyms" synonymlist
synonymlist ::= synonymdeclaration  | synonymlist synonymdeclaration
synonymdeclaration ::= idlist "=" ID "."
messages ::= "message" messagelist
messagelist ::= message  | messagelist message
message ::= ID ":" statements
syntax ::= "syntax" syntaxlist
syntaxlist ::= syntaxitem  | syntaxlist syntaxitem
syntaxitem ::= ID "=" syntaxelements optionalsyntaxrestrictions
syntaxelements ::= syntaxelement  | syntaxelements syntaxelement
syntaxelement ::= ID  | "(" ID ")" optionalindicators
optionalindicators ::=  | optionalindicators indicator
indicator ::= "*"  | "!"
syntaxrestrictionclauses ::= syntaxrestriction  | syntaxrestrictionclauses "and" syntaxrestriction
syntaxrestriction ::= ID "isa" restrictionclass "else" statements
restrictionclass ::= ID  | "container"
optionalsyntaxrestrictions ::= "."  | "where" syntaxrestrictionclauses
verb ::= verbheader verbbody verbtail
verbheader ::= optionalmeta "verb" idlist
optionalmeta ::=  | "meta"
verbbody ::= simpleverbbody  | verbalternatives
verbalternatives ::= verbalternative  | verbalternatives verbalternative
verbalternative ::= "when" ID simpleverbbody
simpleverbbody ::= optionalchecks optionaldoes
verbtail ::= "end" "verb" optionalid "."
optionalchecks ::=  | "check" statements  | "check" checklist
checklist ::= check  | checklist "and" check
check ::= expression "else" statements
optionaldoes ::=  | does
does ::= "does" optionalqual statements
class ::= "every" ID optionalheritage properties classtail
classtail ::= "end" "every" optionalid genSym1
genSym1 ::=  | "."
addition ::= "add" "to" genSym2 ID optionalheritage properties addtail
genSym2 ::=  | "every"
addtail ::= "end" "add" genSym3 optionalid genSym4
genSym3 ::=  | "to"
genSym4 ::=  | "."
instance ::= "the" ID optionalheritage properties instancetail
instancetail ::= "end" "the" optionalid genSym5
genSym5 ::=  | "."
optionalheritage ::=  | heritage
heritage ::= "isa" ID genSym6
genSym6 ::=  | "."
properties ::=  | properties property
property ::= where genSym7  | containerproperties  | description  | genSym8 articleorform  | name  | pronoun  | initialize  | entered  | mentioned  | "definite" articleorform  | "negative" articleorform  | is attributes  | script  | exit  | verb
genSym7 ::=  | "."
genSym8 ::=  | "indefinite"
exit ::= "exit" idlist "to" ID optionalexitbody "."
optionalexitbody ::=  | optionalchecks optionaldoes "end" "exit" optionalid
is ::= "is"  | "are"  | "has"  | "can"
optionaldescription ::=  | description
description ::= "description" optionalchecks optionaldoes  | "description" statements
articleorform ::= article  | form
article ::= "article"  | "article" statements
form ::= "form"  | "form" statements
entered ::= "entered" statements
initialize ::= "initialize" statements
mentioned ::= "mentioned" statements
name ::= "name" ids optionalfullstop
pronoun ::= "pronoun" idlist optionalfullstop
optionalfullstop ::=  | "."
containerproperties ::= genSym9 optionallyopaque "container" containerbody
genSym9 ::=  | "with"
optionallyopaque ::=  | "opaque"
containerbody ::= optionaltaking optionallimits optionalheader optionalempty optionalextract  | "."
optionaltaking ::=  | "taking" ID "."
optionallimits ::=  | "limits" limits
limits ::= limit  | limits limit
limit ::= limitattribute elseorthen statements
elseorthen ::= "else"  | "then"
limitattribute ::= attributedefinition  | "count" Integer
optionalheader ::=  | "header" statements
optionalempty ::=  | "else" statements
optionalextract ::=  | "extract" optionalchecks optionaldoes  | "extract" statements
event ::= eventheader statements eventtail
eventheader ::= "event" ID
eventtail ::= "end" "event" optionalid "."
script ::= "script" ID genSym10 optionaldescription steplist
genSym10 ::=  | "."
steplist ::= step  | steplist step
step ::= "step" statements  | "step" "after" expression genSym11 statements  | "step" "wait" "until" expression genSym12 statements
genSym11 ::=  | "."
genSym12 ::=  | "."
rule ::= "when" expression then statements optionalendwhen
then ::= "=>"  | "then"
optionalendwhen ::=  | "end" "when" genSym13
genSym13 ::=  | "."
start ::= "start" where "." optionalstatements
optionalstatements ::=  | statements
statements ::= statement  | statements statement
statement ::= outputstatement  | specialstatement  | manipulationstatement  | actorstatement  | eventstatement  | assignmentstatement  | repetitionstatement  | conditionalstatement
outputstatement ::= STRING  | "describe" what "."  | "say" sayform expression "."  | "list" primary "."  | "show" ID "."  | "play" ID "."  | "style" ID "."
sayform ::=  | "the"  | "an"  | "it"  | "no"
manipulationstatement ::= "empty" primary optionalwhere "."  | "locate" primary where "."  | "include" primary "in" what "."  | "exclude" primary "from" what "."
eventstatement ::= "cancel" what "."  | "schedule" what optionalwhere "after" expression "."
assignmentstatement ::= "make" primary something "."  | "strip" optionalfirstorlast optionalexpression optionalwordorcharacter "from" expression optionalinto "."  | "increase" attributereference optionalbyclause "."  | "decrease" attributereference optionalbyclause "."  | "set" attributereference "to" expression "."
optionalbyclause ::=  | "by" expression
optionalfirstorlast ::=  | "first"  | "last"
optionalwordorcharacter ::=  | "word"  | "words"  | "character"  | "characters"
optionalinto ::=  | "into" expression
conditionalstatement ::= ifstatement  | dependingstatement
ifstatement ::= "if" expression "then" statements optionalelsiflist optionalelsepart "end" "if" "."
optionalelsiflist ::=  | elsiflist
elsiflist ::= "elsif" expression "then" statements  | elsiflist "elsif" expression "then" statements
optionalelsepart ::=  | "else" statements
dependingstatement ::= "depending" "on" primary dependcases "end" genSym14 "."
genSym14 ::= "depend"  | "depending"
dependcases ::= dependcase  | dependcases dependcase
dependcase ::= "else" statements  | righthandside "then" statements
repetitionstatement ::= foreach ID optionalloopfilters "do" statements "end" foreach genSym15
genSym15 ::=  | "."
optionalloopfilters ::=  | filters  | "between" arithmetic "and" arithmetic
foreach ::= "for"  | "each"  | "for" "each"
actorstatement ::= "stop" what "."  | "use" "script" ID optionalforactor "."
optionalforactor ::=  | "for" what
specialstatement ::= "quit" "."  | "look" "."  | "save" "."  | "restore" "."  | "restart" "."  | "score" optionalinteger "."  | "transcript" onoroff "."  | "system" STRING "."  | "visits" Integer "."
onoroff ::= "on"  | "off"
optionalexpression ::=  | expression
expression ::= term  | expression "or" term
term ::= factor  | term "and" factor
factor ::= arithmetic  | factor optionalnot where  | factor optionalnot relop arithmetic  | factor optionalnot "contains" arithmetic  | factor optionalnot "between" arithmetic "and" arithmetic
arithmetic ::= primary  | aggregate filters  | primary "isa" ID  | primary is something  | arithmetic binop primary
filters ::= filter  | filters "," filter
filter ::= optionalnot where  | optionalnot "isa" ID  | is something
righthandside ::= filter  | optionalnot relop primary  | optionalnot "contains" factor  | optionalnot "between" arithmetic "and" arithmetic
primary ::= STRING  | what  | "score"  | optionalminus Integer  | "{" optionalmembers "}"  | "(" expression ")"  | "random" optionaltransitivity "in" primary  | "random" primary "to" primary
aggregate ::= "count"  | aggregator "of" ID
aggregator ::= "max"  | "min"  | "sum"
something ::= optionalnot ID
what ::= simplewhat  | attributereference
simplewhat ::= ID  | "this"  | "current" "actor"  | "current" "location"
attributereference ::= ID "of" what  | what ":" ID
optionalwhere ::=  | where
where ::= optionaltransitivity "here"  | optionaltransitivity "nearby"  | optionaltransitivity "at" primary  | optionaltransitivity "in" primary  | optionaltransitivity "near" what
binop ::= "+"  | "-"  | "*"  | "/"
relop ::= "<>"  | "="  | "=="  | ">="  | "<="  | ">"  | "<"
optionalqual ::=  | "before"  | "after"  | "only"
optionalnot ::=  | "not"
optionaltransitivity ::=  | "transitively"  | "directly"  | "indirectly"
optionalid ::=  | ID
ids ::= ID  | ids ID
idlist ::= ID  | idlist "," ID
optionalinteger ::=  | Integer
optionalminus ::=  | "-"
ID ::= Identifier  | "location"  | "actor"  | "opaque"  | "visits"  | "contains"  | "on"  | "it"  | "of"  | "first"  | "into"  | "taking"  | "off"

tab ::= '\t'
lf ::= '\n'
cr ::= '\r'
zero ::= '0'
zeroToThree ::= zero  '+' "123"
octalDigit ::= zero  '+' "1234567"
nonZeroDigit ::= "123456789"
digit ::= '0'  '+' nonZeroDigit
hexDigit ::= digit  '+' "ABCDEFabcdef"
letter ::= 'A'  .. 'Z'  '+' 'a'  .. 'z'  '+' '_'  '+' '$'  '+' '\u00e0'  .. '\u00f6'  '+' '\u00f8'  .. '\u00fe'
char ::= ANY  '-' "'"  '-' '\'  '-' cr  '-' lf
stringChar ::= ANY  '-' '"'
Identifier  ::=  ( letter  ( letter  | digit  | '_'  )*  )  |  ( "'"  ( char  )* "'"  )
Integer  ::= digit  ( digit  )*
STRING  ::= '"'  ( stringChar  | '""'  )* '"'
Import  ::= "import"

Build 2015 Breaks Parsing of Accented Letters

The new dev build 2015 has broken the parsing of accented letter (for sure, the ì).

Look at the Italian library, where there's a verb ("say"), and how the introduction of build 2015 broke the test suite:

  • commit ecfe9da (switched to build 2015) is broken:

      ```
      > dì "ciao" a 1
      Non conosco la parola 'd'.
      ```
    
  • commit 340bb40 (build 1980) was working:

      ```
      > dì "ciao" a 1
      Dici "ciao", ma non succede nulla.
      ```
    

I'm not sure if this also affects other accented letters (à, è, é, ò, ù), but I'll check.

Add NOP Instruction

REQUEST

Add a new NOP instruction to do nothing.

Possible syntax: NOP, DO NOTHING, NOTHING.

RATIONALE

In various occasions I've come across the need of a NOP instructions, e.g. when I'm forced to rely on the ELSE part of a condition containing an expression that can't be NOT-evaluated, or because I needed a temporary placeholder while drafting some code changes.

As a practical example, class expression can only be positively evaluated, so it's not possible to use If X NOT IsA someclass (e.g. when trying to affect only instances of the ancestor class, when targeting an attribute that is inherited along the line).

The closest thing to a NOP right now is printing an empty string, but this is not possible inside RULEs:

WHEN location OF hero IS lit
  THEN
    IF location OF hero IsA cave
      THEN "" -- do nothing! (doesn't work)
      ELSE
        -- Whatever needs to be done on `lit` locations that
        -- are not cave instances...

The above example (possibly not 100% correct) roughly illustrates where a NOP instruction would be useful.

Hopefully, implementing a NOP shouldn't be too complex, nor have unexpected side effects (since it does nothing), and wouldn't affect backward compatibility.

Buggy Behaviour with Limit Attributes?

In test_indirect-containment.a3t you'll find a test which reveals inconsistent behaviour with attributes limits.

In the example, the Hero can carry max 20 units of weight; in the room every object weighs 10 units, so he can't carry more than two of those objects.

But there's a bag which is a weightless container without weight carrying limits.

If the hero puts 3 objects in the bag and then picks it up, ALAN doesn't complaint about the fact that the bags contents weigh more than the limit.

On the other hand, if the hero is carrying the empty bag, and tries to put those same three objects into it, ALAN won't allow the third one since the limit was already reached.

I seem to understand (from other tests in that file) that when it comes to items counts limits, a container will always count as a single item, regardless of its contents (e.g. the hero can carry only 3 objects, but one of them can be a bag with 20 objects).

I'm not sure if this is intentional by design (e.g. to allow carry-alls), but when it comes to weight (i.e. any attributes for limits) it makes little sense to count just an item's weight without the weight of its contents. So I'm not sure how the above inconsistency should have resolved.

Add Clear-Screen Instruction

NOTE — This was already discussed in the mailing list, just dropping it here as a reminder, since the list archive is no longer on Yahoo.


FEATURE REQUEST

Add an instruction to clear the screen/terminal — e.g. CLEAR, CLEAR SCREEN, CLS.

Like multimedia and text styling instructions, the operation won't be guaranteed on all interpreters; but it should work fine on Glk-based terps, and possibly also on most Shells/terminal emulators.

Special Handling of Directions and their Aliases

@thoni56, I wanted to expose a problem I've encountered with the ALAN Italian project, regarding the fact that ALAN doesn't allow having same-worded aliases, verbs, directions or special classes of known words (e.g. noise words, etc.).

  1. Handle directions and their aliases separately from verbs and other special words, allowing duplicate entries.
  2. When the parser encounters a single-word input, assume it's a direction before trying to match it to a global verb.
  3. If the single-word input matches both a direction and a global verb, throw a disambiguation request.

The Problem

The problem affects mostly directions shorthand, which clash with other common verbs or special words. To better illustrate my example, here's a table with all the main direction in Italian and English.

direction short English clashes with
nord n north
nordest ne northeast
est e east with "e" the AND_WORD.
sudest se southeast
sud s south
sudovest so southwest
ovest o west
nordovest no northwest the "no" reply.
su up
giù down

Beside the above mentioned clashes, which affect ALAN Italian practically, the direction shorthands "se" and "o" could also potentially clash with other Italian constructs — "se" means if, and "o" means or. Although I didn't encounter practical cases of the latter conflicts in real IF games, it does highlight the extent to which directions shorthand can collide with other useful and common Italian words.

I couldn't come up with any practical solution to provide directions shorthands in Italian adventures — every possible solution seems to solve some conflicts but introduce newer ones. E.g. if I decide to include an extra letter:

direction short English clashes with
nord no north the "no" reply.
nordest nes northeast
est es east
sudest ses southeast
sud su south with "su" the up direction.
sudovest sov southwest
ovest ov west
nordovest nov northwest

I've tried many other solutions, but it simply doesn't seem possible to avoid clashes while attempting to use a coherent system. Since in most languages short words represent commonly used particles, adverbs, etc., it might just as well be possible that similar problems affect other locales too — and that it might be a lucky exception that in English those directions shorthands don't clash with other words that are important to IF gameplay.

The Solution

From an IF point o view, directional commands always contain just the direction, so I was wondering if it would be possible to change how ALAN treats directions and their aliases, compared to other game objects.

If ALAN were to handle directions and their aliases as a separate category of words, when faced with a single word input the parser could first assume that it's a direction command, or an alias thereof, before attempting to match it with a verb.

This wouldn't solve the conflict between "no" the direction (northwest) and "no" the reply (to a yes/no question), but only if the "no" reply was implemented in an adventure as a raw reply — whereas if implemented as reply no or reply yes, there wouldn't be any conflict since the reply would no longer be a single word input.

As for "e" (short for east) conflicting with "e" the AND_WORD, if the parser was to consider first the possibility of it being a direction alias, the conflict would be solved — also, it wouldn't make sense to use an AND word in an input sentence with less than three word.

I'm not sure how complex these changes would be, or whether they might have an unexpected impact on backward compatibility, but having a separate list for direction words and their aliases, and the parser attempting to first match a directional command for single-word inputs, seems a reasonable change; also, the parser could always check if there's also a same-worded verb and throw a disambiguation request if this was the case — but as mentioned above, authors could easily avoid these edge cases by formulating fuller verbs (e.g. reply no, answer no).

As for other direction commands, like go no, the parser already strips the NOISE_WORD "go" from the input, and the same can be achieved in any locale — also, I believe most players will type just the direction anyhow.

Alan v2 interpreter license

While Alan v2 is obsolete, Gargoyle still includes an interpreter for older games that were created using it, thus my interest here.

The Alan v2 interpreter (in the v2_x-updates branch) doesn't work on modern 64-bit systems, as it assumes pointers are 32 bits. Gargoyle made some changes a while back to fix issues with pointers on the stack, but there are still problems: the clock.alan regression test, for example, segfaults under Gargoyle due to another pointer issue.

I was working on making it 64-bit clean, planning on getting some minimal fixes into the v2 branch here, and then into Gargoyle. But I got kind of carried away and rewrote large chunks of the interpreter, some necessary for the 64-bit fixes, which were a bit difficult to get done, some not necessary for the fixes.

Anyway, since I know you're not maintaining v2 given that v3's been out forever, I plan on maintaining this version myself, but I see that the v2 source contains no license (neither in the Git branch nor the interpreter287src.tgz archive), at least as far as I can tell.

Can you clarify what license the v2 source code is under, please? I don't want to distribute something I have no right to.

Q. About String Attributes Uses and Abuses

@thoni56,

I wanted to ask you some advice regarding the use of string attributes in the Italian StdLib translation.

The Alan Manual mentions in Ch.3, under Numeric and String Attributes:

Note that string type attributes are mainly intended for saving string parameters from the player input, like in:

> scribble "Kilroy was here" on the wall

They are not intended for storing long strings of descriptions, especially not as attributes to classes, as they (in the current implementation) require memory and take time to initialise when starting the game.

Yet, the StdLib makes ample use of string attributes (defined on the mygame location) to store verb responses shared across multiple verbs.

I personally find this approach quite useful, and I've take this approach a step further. Not only I've defined as string attributes any message that appears in more than one verb, but also useful snippets which are recurrent in various messages.

The obvious advantage of this approach is that it allows to change a single string attribute in order to tweak the same message in every verb. Another advantage could be less memory consumption. But after having read the above statement, I'm now in doubt ...

What would be a good rule of thumb regarding such a use of string attributes?

  • If a rather long sentence is shared across multiple verbs (e.g. appears 4 or 6 times), is it worth it?
  • If a short snippet recurs in many places (dozens or more), is it justified to store it as a string attribute?
  • Does the above quote from the Manual still apply to present day computers, which are faster and have larger memory?
  • What would be the criteria to decide when to use a string attribute in a verb body, and when to use a plain string instead?

Since I'm about to go over the final revision of the Alan Italian module that deals with verb messages stored as string attributes, I'd really benefit from your advice on this. Maybe it's also worth expanding on this in the Alan Manual as well.

Thanks!

Nested Locations Bug: Verb Parameters out of Scope

ALAN 3.0beta8 build 2276

@thoni56, I've come across an odd bug relating to nested locations and parameters scope — when commanding some verbs with a parameter located in the outer location I get:

APPLICATION ERROR: Nonexistent parameter referenced.

but only for certain objects, and other verbs with very similar syntaxes work fine. It's a strange case, as you'll see.

ALAN i18n: BUG Branch

Since I've stumbled upon the bug while working on a dev branch for Foundation Italian, I've created a copy of the branch at the exact commit where the bug appeared, so you have all the time to test it even if I carry on my dev work:

Bug Related Files

The bug occurs in the Italian Cloak of Darkness source (LL 172):

Bug Description

Unfortunately, the adventure is in Italian, so I'll provide a brief description of where the problem show up, and what the adventure is trying to do there.

The teatro location is a sort of "region location" which contains all other game rooms, so that I can implement scenery objects like ceiling, walls and floor at teatro and make them visible everywhere in the game:

  • object paretiwalls
  • object pavimentofloor
  • object soffittoceiling

The above objects are all scenery (i.e. using the new scenario attribute that replaced the old class). Furthermore, I use location based CHECKs as a trick to customize some verb responses in specific locations, e.g.:

The pareti IsA object at teatro.
  Is scenario.
  Has articolo "le".
  Verb esaminare
    Check current location <> guardaroba
      else say descrizione_pareti of guardaroba.
  End verb.
End the pareti.

This system used to work, even with the new scenery attribute, until I amended the Italian Foundation on main branch (just renamed some attributes related to scenery and their messages) and then rebased ... after the rebase some library verbs stopped working with pavimento and soffitto (but not with pareti strangely) resulting in the following crash (see testa_scenografie_stanze.a3t):

> prendi il pavimento


As you enter the twilight zone of Adventures, you stumble and fall to
your knees. In front of you, you can vaguely see the outlines of an
Adventure that never was.

APPLICATION ERROR: Nonexistent parameter referenced.

<If you are playing this piece of Interactive Fiction, please help the
author to debug this programming error. Send an exact transcript of the
commands that led to this error to the author. Thank you! If you *are*
the author, then you have to figure this out before releasing the game.>

It's very strange that only some verbs like prendere (take) and leggere (read) — maybe others too — cause the crash while other similar verbs don't, and even stranger that these same verbs work fine with the pareti (walls) which is implemented in an almost identical way. Even stranger, before the amend on main and the rebase, they all worked fine.

NOTE — The Syntax of prendere (take) has a multiple indicator parameter, but not leggere (read).

So, it looks like it's a nasty edge-case bug — it could be due to some small overlooked problem in the Italian library code, which happens not to be covered by the test suite, or some bug in the Cloak source code (which is still kind of messy, since I've ported it from the StdLib Italian code). But still, it's something that the compiler should have probably caught at compile time, since the adventure only crashes when these verbs are used on those specific objects.

Anyhow, I have no idea on how to find the root of the bug using the ALAN debugger, and after having fiddled with the sources for hours I gave up hope of spotting the problem, so I'm asking you if you can have a look and determine whether it's a problem with the library, the Cloak source code or with ALAN/ARun.

WinARun: Inconsistent Style for Room Names in Brief Mode

@thoni56, I'm not sure whether this is a bug or it's intended to be so by design, but I'm reporting it anyway.

While testing the AGAIN run-time message I noticed that when a brief room description is shown (using Visits 1) in WinARun, the room name is not styled as usual (i.e. "title style") but as normal text.

Is that normal? Shouldn't the room name always use the title style, even when in non-verbose modality (i.e. only the room description needs being omitted).

As it happens, I was testing AGAIN in an adventure which prints some fairly long text when entering and exiting a location, so it was hard for me to spot the room name (with the added AGAIN message) because I was expecting the usual bold style for room names.

IMO, a room name should have consistent styling, regardless of whether it's in brief or verbose mode.

EXIT BUGS: $v and $o

Bug Description

Inside an EXIT block, the $v is printed out as #literal, instead of the typed direction.

Expected Behavior

Although directions are not technically VERBs, but EXITs, it would make sense to ensure that in their runtime context the $v is assigned the direction typed by the player — after all, from the player's point of view, there are only "typed commands", and the distinction between verbs and directions is not meaningful.

Example to Reproduce the Bug

When compiling and adventure containing the following code:

The forest IsA DARK_LOCATION at outdoor.
  Exit north, south, east, west, northeast, southeast, northwest, southwest
    to nowhere
      Check "You head $v, but you only find more trees."
  End exit.
End the forest.

it produces the following transcript:

> north
You head #literal, but you only find more trees.

Secondary Bug (related)

Even worst, using the $o — which manages to slip through the compiler's integrity checks:

The forest IsA DARK_LOCATION at outdoor.
  Exit north, south, east, west, northeast, southeast, northwest, southwest
    to nowhere
      Check "You head $o, but you only find more trees."
  End exit.
End the forest.

produces:

> north
You head

As you enter the twilight zone of Adventures, you stumble and fall to your
knees. In front of you, you can vaguely see the outlines of an Adventure that
never was.

APPLICATION ERROR: Nonexistent parameter referenced.

<If you are playing this piece of Interactive Fiction, please help the author to
debug this programming error. Send an exact transcript of the commands that led
to this error to the author. Thank you! If you *are* the author, then you have
to figure this out before releasing the game.>

Not so with $1, which produces a compiler error instead:

   25.        Check "You head $1, but you only find more trees."
=====>              1

  *1*   551 E : String contains reference to a parameter that does not exist in
                this context.

Latest Dev Snapshots for Windows are Broken

I've tried downloading the ALAN Alpha SDK build2105 but encountered numerous problems:

  • alan3.0beta8-2105.win32.i686.zip:
    • alan.exe fails to run — Application Error: The application was unable to start correctly (0x000007b).
  • winarun3.0beta8-2105.win32.i686.setup.exe:
    • After installation, the terp can't be launched (nothing happens).

arun.exe seems to work fine, but couldn't really test it properly.

I've tried using WinARun from build 2103, but it didn't launch either, so I fear there's a problem with the build toolchain that has been affecting more than one dev snapshot.

TAKE ALL Crashes with Container Limits

ALAN 3.0beta8 build 2220

Attempting TAKE ALL will crash the adventure is the picked items exceed the Hero's Container Limits Count by at least two more items than maximum allowed.

E.g. here the Hero has a limit of max two objects, when picking the third one it will print the warning, but on the fourth it crashes:

Bedroom
There is a ball, a bottle, a letter and a pie here.

> take all
(ball) You pick up the ball.

(bottle) You pick up the bottle.

(letter) You pick up the letter. You can't carry more than two items!

(pie)

As you enter the twilight zone of Adventures, you stumble and fall to your
knees. In front of you, you can vaguely see the outlines of an Adventure that
never was.

APPLICATION ERROR: Interpreter recursion.

<If you are playing this piece of Interactive Fiction, please help the author to
debug this programming error. Send an exact transcript of the commands that led
to this error to the author. Thank you! If you *are* the author, then you have
to figure this out before releasing the game.> 

The above transcript was generated from the following adventure:

The hero IsA actor
  Container
    Limits Count 2
      then "You can't carry more than two items!"
End the hero.


The Bedroom IsA location
End the Bedroom.

The ball IsA object at Bedroom.
End the.

The bottle IsA object at Bedroom.
End the.

The letter IsA object at Bedroom.
End the.

The pie IsA object at Bedroom.
End the.


Syntax take = take (obj)*
  Where obj IsA object
    else "You can only handle objects."

Add to every object
  Verb take
    Does
      "You pick up $+1."
      Locate obj in hero.
  End verb.
End add to.

Start at Bedroom.

Adopt ".a3log" and ".a3sol" Extensions for Transcripts and Commands Scripts

Feature Request

  • In all Alan terps:
    • Use the .a3sol extension for generated commands scripts.
    • Use the .a3log extension for generated transcripts.
  • In Arun, allow using both -l and -c switches in the same invocation, to generate both a transcript and a commands script during the same game session (using the above extensions to distinguish them).

By no means these extensions should be enforced on end users, they should be just the default extensions used by Arun and WinArun, with users being free to change them according to their will (just as it's today).

The proposal is more about providing official Alan extensions for transcripts and commands scripts which are more in line with the other native Alan 3 extensions (.a3c amd .a3r), making them uniquely identifiable as Alan related files, and with all the benefits listed below (the most important being the ability to configure editors to handle them as ISO-8859-1 files, and being aware of their context).

Rationale

  • Intuitiveness
    • The .a3sol extension for commands script is more intuitive than just .log, for the .sol extension is commonly found in IF commands scripts (more often referred to as solution files).
    • The .txt extension proposed by WinArun is not suggestive of an IF transcript nor commands script (unlike .a3log and .a3sol), it's just the most common text file extension used on Windows.
  • Consistency
    • The .a3sol and .a3log extensions are more consistent with the other Alan file extensions (.a3c amd .a3r), and they can be more intuitively associated with Alan in a single glance.
    • Currently Arun and WinArun use different default extensions for generated transcripts and commands scripts (.log the former, .txt the latter). Adopting these new extensions would add more consistency across the various Alan terps.
  • Arun Benefits — Because Arun currently uses the .log extension for generating both transcripts and commands scripts, it can't create both in a single session via the -l -c switches (only one of them will be honored), forcing the player to first create a commands script, and then generate a transcript using the former.
  • Editors Encoding — Because Alan requires ISO-8859-1 compliant files, using univoque extensions is far better than using common extensions like .txt or .log, which are often associated to UTF-8 encoding on most editors.
  • Improved IDE Experience — Using different extensions for transcripts and commands scripts allows a better workflow with editors. As an example, see how the Sublime Alan package (created by me) can handle the .a3sol and .a3log extensions smartly, both in terms of custom color schemes, syntax highlighting, auto-completions and snippets, as well as encoding settings (ISO-8859-1).
  • Git Repos — The .log extension generated by Arun is not ideal for Git repository contents, which often add the *.log pattern to .gitignore, or may have other auto-generated logs with the same extension (and often different EOL normalization settings).

Q: About ALAN's Parser & Lexer Generators

@thoni56, In #12 you mentioned

The character sets used are actually generated by the scanner generator ("lexem parser generator") that is part of the legacy compiler-compiler toolset that is used to generate the scanner (and parser) for the Alan compiler.

I've been trying to find info about this compiler-compiler tool, but couldn't find anything by searching "lexem parser generator". I noticed that in the generated files pmParse.c and smScan.c it mentions the tools "ParserMaker" and "ScannerMaker", but couldn't find anything for those either on Google.

Not much luck with Wikipedia's page Comparison of parser generators either.

Could you provide me with more info about this toolset and its author, and a link to its website (if still in existence) or some place where its original codebase can still be found?

I would have liked to add to our Wiki (here) a page with some info about it, since its part of the required tools to build ALAN, and because these tools are now old and not easy to find info about.

Is this compiler-compiler tool (which I believe you've had to modify in order to adapt it to the latest changes) included in this repository? i.e. how would someone wanting to update the ALAN grammar and parser/lexer have to go about it? (I'm guessing he/she would need access to the same parser/lexer generator tools).

Thanks.

Transcript is always empty when started from code

If you start a transcript by typing SCRIPT ON (as opposed to starting it with the -l command line option) a transcript file is created but never printed to, so it stays empty.

The reason seems to be that the global transcriptOption is never set to true.

I haven't studied the code closely, but if I just add transcriptOption = true; to line 735 in exe.c, it works as it should, as far as I can tell.

Enable Wiki

Ciao Thomas!

Great to see the ALAN respository on GitHub! I've added a link to the various ALAN related projects, alongside the Bitbucket repository.

I propose to open the Wiki for public editing, so we might use it for documenting the ALAN project — compiling instructions, info on its dependencies, and maybe even document the AMachine in the course of time.

I could contribute to the Wiki by adding the welcome page, useful links, and any material for which I have enough knowledge to write about.

What do you think?

Allow Interrupting the VERBs' Execution Chain

NOTE — This was already discussed somewhere (possibly in the mailing list) but I'm adding it here to make it a "formal" feature request/discussion on the topic.


ALAN's VERBs system is quite flexible thanks to the DOES [ONLY/BEFORE/AFTER] construct, allowing to exploit ONLY to supersede certain actions in special context (a given actor, location or object), and BEFORE/AFTER allow adding extra messages or actions to a verb execution (e.g. an extra printed message for certain things or locations, etc.).

The only limitation I see in the current system is the lack of an instruction that can arbitrarily interrupt the verb execution at any point of of the DOES [BEFORE/AFTER] chain.

Right now, one can only use CHECKs to prevent the action and print a message, but in some circumstances it might be desirable to allow the various DOES block to execute as normal, and have a specific DOES [BEFORE/AFTER] block interrupt the execution so that it does not go any further along the chain.

Surely, the current system covers the general and broader needs of most adventures. But contexts like a library or a big-sized adventure might introduce added complexity where the ability to ensure the execution interruption at a specific point (without preventing what comes before it) could be really useful.

It's also worth noting that most IF authoring system (which all have a very similar approach to verbs execution) do in fact provide the means to abort the execution — either by a specific keyword (e.g. Inform 7) or by returning true/false from the execution block (e.g. Hugo).

Compiler Bugs with Block Comments

@thoni56, I've come across some odd bugs with block comments.

Using ALAN 3.0beta8 build 2207 under Win 10, tested with both CMD and Bash for Windows (same result); source file is UTF-8 BOM, using native CRLF EOL.

Compiled using both alan sample.alan and alan -encoding utf8 sample.alan, same results (the problem is not autodetection of UTF-8 via the BOM).

The problem is due to the fact that the source file has CRLF line ending, if I switch to Unix style LF it works fine (but only if I add a blank line at the beginning).

So there seem to be different problems at stake here: incorrect handling of CRLF EOL (even under the CMD), and a bug when a source file starts with a block comment.

Below are the actual error reports, although they don't really pin-point the problem, but they might help you gain insight on what goes on behind the scenes...

Error 1

With a source file starting with this block comment:

//// "The Barracks" | Alan Beta8 ///////////////////////////////////////////////
A sample ALAN adventure, by Tristano Ajmone,
////////////////////////////////////////////////////////////////////////////////

I get the following compiler errors:

sample.alan

    1.  //// "The Barracks" | Alan Beta8 ///////////////////////////////////
        ////////////
=====>  1    2              3 4

  *1*   103 E : Syntax error. Replacing "/ / / /" with "start here .".
  *2*   211 E : Adventure must start at an instance inheriting from 'location'.
  *3*   102 F : Syntax error. Ignoring "Unknown Token".
  *4*   102 E : Syntax error. Ignoring "<identifier> <identifier> / / / ...".

    3.  ///////////////////////////////////////////////////////////////////////
        /////////
=====>  1

  *1*   155 E : Unterminated block comment. Must end with a line consisting of
                at least four slashes and nothing but slashes.


        5 error(s).
        No detected warnings.
        2 informational message(s).

Note that the error at line 1 contains a  char, so it seems

Error 2

If I add an empty line at the source start, right before the comment block:


//// "The Barracks" | Alan Beta8 ///////////////////////////////////////////////
A sample ALAN adventure, by Tristano Ajmone,
////////////////////////////////////////////////////////////////////////////////

the compiler error changes slightly:

sample.alan

    2.  //// "The Barracks" | Alan Beta8 //////////////////////////////////////
        /////////
=====>  1

  *1*   155 E : Unterminated block comment. Must end with a line consisting of
                at least four slashes and nothing but slashes.

   66.
=====>  1

  *1*   101 E : Syntax error. Inserting "start here ." before this token.
  *1*   211 E : Adventure must start at an instance inheriting from 'location'.


        3 error(s).
        No detected warnings.
        1 informational message(s).

Division by Zero Crashes all Terps

I was testing an adventure that implements verbs to carry out the four basic operations of arithmetic and noticed that divide by zero crashes all the interpreters.

Possibly it's a bug, for there should be some failsafe mechanism to prevent similar illegal operations — after all, this was a legit example where the context could provide such cases.

Here's the code:

THE kitchen IsA LOCATION
END THE kitchen.

SYNTAX 'divide' = 'divide' (num1) 'by' (num2)
  WHERE num1 IsA integer
    ELSE "You can only use numbers with this verb!"
  AND num2 IsA integer
    ELSE "You can only use numbers with this verb!"

ADD TO EVERY integer
  VERB 'divide'
    When num1
      DOES
        "Sure, $1 divided by $2 equals" SAY num1 / num2. "."
  END VERB 'divide'.
END ADD TO integer.

Start at kitchen.

Tested with the following command:

> divide 4 by 0

Which caused all three terps to crash (WinArun, Gargoyle and ARun). Arun would show the response up to "Sure, 4 divided by 0 equals" before crashing.

Of course, the easy fix was adding the following Check:

  Verb 'divide'
    Check num2 <> 0
      Else "You can't divide by zero!"

But having the interpreter check for a division by zero would be better — at least it should exit with an error, instead of just crashing silently, so that the error message could help the author trace the error and its nature.

Switches Consistency in Alan and ARun

Currently (3.0beta6 build 2015) the Alan compiler and ARun use slightly different switches for the same options, which is rather inconsistent and confusing.

Examples:

option alan arun
version -version --version
help -help none
verbosity -verbose -v

I think that, for consistency sake with Alan compiler, ARun should also allow (and list among the switches) -help; and that -version should replace --version — it looks like the double hyphen here is to distinguish between short and long options, whereas in Alan the double hyphen indicates negation of an option, but all this is rather confusing for these two apps are like two twins for the end user.

Alternatively, since Alan also accepts --version, it might make more sense to list that in the help options list, so the proposed switches would be the same in both tools.

Personally, I would have found it to be more intuitive to have -V for "version" and -v for verbose, at least in ARun.

From the stand point of an Alan author who is not a programmer (and couldn't care less about POSIX standards), the difference in switches between the two might be an added burden, especially if he/she doesn't work often with CLI tools.

Also note the following in ARun:

>arun -help
Unrecognized switch, -h

and:

>arun --help
Unrecognized switch, --

it seems that unrecognized switches are being truncated to two chars in the error report. Again, seems to related to the long/short options distinction mentioned before, but the error report gets confusing to an end users not involved in technicalities.

Add Support for UTF-8 Sources and I/O Stream

First of all, I'd like to state that I think Alan should stick to use single-char encoded strings internally (ISO-8859-1 or Mac), for there are no valid reasons to make Alan Unicode aware — the Latin1 charset is more than sufficient to cover needs for most adventures in any western language, and the exceptions are not worth the change and the overhead that Unicode support would introduce in terms of memory and performance.

Having said that, I think that Alan should accept UTF-8 source code, and ARun should handle I/O text streams in UTF-8 by default. Here follows my rationale for this.

Basically, the Alan compiler should be able to read UTF-8 source files, and transcode them to ISO-8859-1 before parsing them. This shouldn't be a huge addition, for ALAN sources are still expected to contain only characters from the ISO range (although comments could contain Unicode, which is cool and wouldn't affect compilation). Basically, any character above 127 should be be decoded back to a single char, which is rather quick operation to do on any input stream — I've seen code examples for this in various languages, and they rarely exceeded 6 lines of code.

Also, ARun should be able to handle I/O streams in UTF-8 too, which would allow to it accept commands scripts in UTF-8, as well as generating transcripts in UTF-8 (which would simplify interactions with other tools).

As for Glk based terps, only the commands scripts and transcripts would have to deal with UTF-8, for the input player would be handled by the Glk interface there.

Whether UTF-8 streams should be the default or optional, ISO and Mac could still be supported (by a CLI option, or by default), but I'm sure that most users would choose UTF-8, especially when working on their favourite editor or IDE. (A similar faith can be seen in TADS3, one of the first IF tools to introduce an push UTF-8, which initially had UTF-8 as an option, but today is mostly used in UTF-8 only).

Editors Support

Most modern code editors assume UTF-8 as the default encoding, and many don't support well ISO-8859-1 — and those which do, they usually don't offer good protection against encoding break-up with paste operations, where pasting from the clipboard often leads to Unicode chars introduction in the document, automatically converting it to UTF-8.

When I was initially working on the Italian Library translation, I've faced frequent code corruptions during editing, until I created an Alan dedicated package for Sublime Text to enforce ISO-8859-1 (and had to open a feature request for it too, to prevent UTF-8 pasting from corrupting the source files). So I'm well aware how this can easily become a disruptive and frustrating issue.

Maybe those who work with English only don't notice this, but anyone writing in a language with accented letters and/or diacritics is soon going to bump into many issues.

LSP Support

The Language Server Protocol is proving a successful idea, and is being adopted everyday by more editors and languages as the protocol of choice for syntax highlighting, linting and even code refactoring.

The problem is that LSP requires all JSON-RPC messages to be sent in UTF-8, and LSP plug-ins working on other encodings are starting to report lot's of bugs and problems, especially in relation to text ranges and positions (which result in wrong coordinates).

See microsoft/language-server-protocol#376 for a long discussion on this.

If ALAN could handle UTF-8 source files, it would open itself to a world of possibilities via LSP — writing a language server for ALAN wouldn't be all that difficult, its syntax is simple enough to allow writing an error tolerant parser to provide syntax highlighting. From there, we could have a binary Language Server for ALAN that could work with any editor and IDE supporting LSP, which would allow to focus all energies on a single package for all editors — and eventually even support the new LSP features for code refactoring.

LSP is a growing standard, with new feature being added in the course of time. The future is clearly heading in that direction, and even if LSP were to be replaced by another protocol in the future, most features will remain similar.

Tools Support

The above also applies to many tools, especially tools related to version control — Git doesn't offer any specific settings for ISO encodings, and most diffing tools also expect UTF-8 text streams today.

In some cases, pipelines could actually corrupt ISO sources.

Toolchains Support

Just take as an example Asciidoctor, which we are using for the Alan documentation, and all the problems we're facing due to ISO-8859-1 encoding in Alan sources and game transcripts.

EDIT: Now Asciidoctor supports use of include:: with ISO files! — Because Asciidoctor doesn't support use of include:: with ISO files (cf. asciidoctor/asciidoctor#3248), the documentation toolchains for the various libraries need to first convert all Alan sources, commands scripts and generated transcript to UTF-8, so that they might be usable in the documentation.

Similar issues are going to be everyday more common, especially with modern tools, for usage of any encodings beside UTF-8 is strongly discourages nowadays (not only in text files, but also for internal string representations):

Malformed EVENT Causes Terp to Freeze/Hang For Ever

I've encountered a bug related to EVENTs and RULEs.

When I've added the following code to a pre-existing adventure, the compiled game would hang forever on entering the level5 location:

WHEN hero AT Level5 =>
  SCHEDULE Level5Event AT Level5 AFTER 0.

EVENT Level5Event
  IF hero at Level5 THEN
    "$pA supernatural voice screams at you:
     $n""Mortal, are you worthy of using the StdLib?"""
    SCHEDULE Level5Event AT Level5 AFTER 0.
  ELSE
    CANCEL Level5Event.
  END IF.
END EVENT.

I wasn't able to provide a link to the full adventure because it was a WIP adventure that wasn't committed to any repository, but I'm positively sure that the above code was the sole culprit of the terp hanging.

Possibly this leads to a stall situation which is not being foreseen by the compiler.

String Parameters: Infinite Recursion due to $1 Expansion

String parameters accept special $ symbols, which can cause infinite recursion when the player types in a string literal the $1 symbol.

Possibly, string paramaters shouldn't be interpreted for special $ symbols at all. For example, escaping quotes in string parameters isn't possible (eg. write "hello ""world""!" is not parsed), so dollar symbols should be treated as raw text too — otherwise, if the player need to type a $ char he/she is likely to get into trouble (see last example).

Example code:

--==============================================================================
-- BUG: String parameters may contain Alan special $ symbols, which can lead to
--      infinite recursion when the player types "$1".
--==============================================================================

THE room IsA LOCATION
  Has text "".
  VERB write
    DOES
      "You write ""$1""."
      Set text of room to str.
  END VERB write.
END THE room.

-- Verb Write
SYNTAX write = write (str)
  WHERE str IsA string
    ELSE "You can only use strings with this verb."


-- Verb Read
SYNTAX read = read.

VERB read
  DOES
    Say text of room.
END VERB read.

Start at room.

test with:

read
write "Hello world."
read
write "Hello.$pWorld"
read
write "Hello.$1"
read
write "$1"

Dollar Symbols Are Expanded

The command write "Hello.$pWorld" clearly demonstrates that $ symbols are expanded in string parameters:


> write "Hello.$pWorld"
You write "Hello.

World".

> read
Hello.

World

$1 Infinite Recursion 1

when it gets to write "Hello.$1" the game session enters infinite recursion and the abruplty crashes:

> write "Hello.$1"
You write "Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.
Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.
Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.
Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.
Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.
Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.
Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.Hello.
[... TERP CRASHES ...]

Whereas by typing write "$1" the terp just hangs until it crashes.

Problems With Literal $ (Currency) Symbol

Also, note that trying using the $ character crashes the terp:

> write "I have $5!"
You write "I have 

As you enter the twilight zone of Adventures, you stumble and fall to your knees. In front of you, you can vaguely see the outlines of an Adventure that never was.

APPLICATION ERROR: Nonexistent parameter referenced.

<If you are playing this piece of Interactive Fiction, please help the author to debug this programming error. Send an exact transcript of the commands that led to this error to the author. Thank you! If you *are* the author, then you have to figure this out before releasing the game.>

Slow performance in The Wyldkynd Project

When playing The Wyldkynd Project, every command gets slower and slower, until you have to wait more than 10 seconds between turns toward the end of the game. In my automatic testing that feeds commands to it at maximum speed, one turn takes longer to complete than most entire games take from start to finish.

This seems to be the case both with current Alan 3 source and with the Alan 3.0beta6 build used by Gargoyle. I'm running macOS 11.5, Intel Core i7 2,6 GHz.

Perhaps this should be reported to Robert DeFord, the writer of the game, instead, but it would be nice to get a confirmation that other people can reproduce it.

Text wrapping problem

In an issue in the alan-doc project Tristano mentioned a possible problem with the line-wrapping calculation:

After some fiddling around with the WIDTH option in the source adventures, I realized that the main problem is that the value is not honored correctly in the generated transcript, which tend to wrap before the expected column.

If set it to 80 it seems to fail to wrap at the longest possible line that doesn't go beyond that value. E.g. it would wrap at 72 even though the following word is 4 character only. The same applies to any other value, it tend to wrap earlier than expected.

Here's a real example generated from the StdLib repo, using the default setting of 80 (adding it explicitly yelded the same results):

The air in this gloomy tunnel is filled with foul smelling fumes. Your
eyes are itching and your lungs burning.

The output is being wrapped at 71, but the correct wrapping for the value 80 should have been:

The air in this gloomy tunnel is filled with foul smelling fumes. Your eyes are
itching and your lungs burning.

As you can see, there was space for two more words on the line.

Possibly it's a problem with the algorithm that handles line wrapping in the interpreter.

Is the width value to be considered as inclusive of the max chars per line? (i.e. 80 = 80 chars, or 79?)

I have the impression that white spaces are being included in the computation, for output lines often have a space as their last character. This might be affecting calculation of the wrapping point, falsifying its value.

ARun: Add IFID Info and Recovery Switches

After having looked into how IFIDs work in ALAN, I gathered the following:

  • The compiler always look for an .ifid file with same-name as the source adventure, and if the file exists (and contains a valid IFID string) it will reuse that IFID again, otherwise it will generate a new IFID.
  • The compiler (from a certain version onward) now also stores a copy of the IFID string after the ACodeHeader, inside the .a3c story file.

The problem is that if the user looses the original .ifid file, he/she has no way to recover it from the story file (except via an hex-editor).

I propose to add to ARun:

  • an -info switch to show some basic info on the .a3c file, including its IFID and the compiler version used.
  • a switch to recover the .ifid file from an .a3c story file — i.e. parse the IFID string contained after the ACodeHeader and recreate the lost text file.

It seems to me that currently the IFID doesn't play a big role in Alan — it's there, but not much visibility is given to it. For example, it would be good to be able to access the IFID from within an adventure in order to be able to print it — via some built-in keyword constant, like IFID. E.g.:

"Adventure IFID:" Say IFID.

The other terps (WinARun, Gargoyle slot, etc.) should also have a menu to show the IFID and compiler version info of the currently running adventure.

Needed: Try/Catch for Dry-Running Instructions

I'm adding here this feature request as a reminder of what was already discussed in AnssiR66/AlanStdLib#57 regarding the need to introduce some syntactic construct that allow testing if a given action would be achievable, before actually carrying it out.

The need rose from the realization that VERBs, SCRIPTs, RULEs and EVENTs might clash with container's constraint when attempting to dis-locate an object from one place to another, and that the failure to EXTRACT the object would interrupt the VERB (etc.) execution without warning.

E.g. some verbs might require implicit taking (e.g. eating or throwing something that the hero doesn't posses but it's in the vicinity). The problem is that currently there's no way to test if attempting to move the object (e.g. into the hero's inventory first) would succeed or fail, so in case of failure the VERB interrupts execution abruptly without allowing to print an adequate message.

Having access to a special instruction that would allow to "dry-run" an instruction, returning true/false, would allow to handle such cases.

In the previous discussions various solution were proposed, along the line of TRY/CATCH, but it didn't develop anywhere beyond drafty ideas. So I though of creating this Issue here, since in the meantime the ALAN repo was moved from Bitbucket to GitHub, and the newsletter archives were deleted from Yahoo.

I've also created the limitations label for this type of Issue, because I vaguely remember an old email exchange proposing to store somewhere the various discussion on current limits of the ALAN language.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.