Development history of Finto

2012-11-29 (start of version A-00)

Start. First prototype. It works great!

2012-11-30

Syntactic sugar, synonyms and abbrs for the narrative. Passage visits count field. Subpassages. Config variables for the keyword's color and marks. If the keyword has a trailing punctuation char, it's removed. Different optional marks for the start and the end of printed keywords.

2012-12-01

Simpler syntax for (sub)passages. The old one was:

:passage
  ( definition )
  ; passage-id passage!

The new one is:

passage-id :passage
  ( definition )
  ;passage

Clearer definition and management of the keywords table.

2012-12-02

Clearer code for command input.

Refactorized code for passage and subpassage definitions.

Fixed: if the last char of a keyword is a punctuation char, it's not included in the link marks.

Configurable link colors.

Configurable link uppercaseness.

UTF-8 support.

Case insensitive: Keywords and commands are converted to lowercase.

2012-12-03

The this value is implemented in (sub)passage definitions.

The passages counter is updated every time a passage id is created.

2012-12-04

A link is created only when its maximum number of visits has not been reached.

Keywords are copied (with >sb) to the string buffer before storing. Otherwise they may be modifed in their original location in the dictionary.

2012-12-10

Conditional links: ?link and ?+link.

2012-12-12

Fixed: 3drop was missing.

2012-12-17

Fixed: keyword! stored the keyword before changing it to lowercase.

2012-12-18

Fixed: now -trailing_punctuation checks empty strings; this finally makes empty links possible.

2013-05-03

Some changes in comments.

2013-05-22

Stack notation. finto-voc.

2013-05-23

Improvement: link? factored out.

Impromevent: link and +link rewritten: now invisible links point to the latest linked passage; this makes the narrative syntax easiero, because the one passage id is enough for any number of links (the visible link and any amount of invisible links).

Fix: ?link now prints the link text when the link cannot be created.

New: ?|link and ?|l.

New: | as alias for print_cr.

2013-05-26

New: Completed the parsing forms for printing and linking.

Change: Removed all combined forms of printing and linking in a new paragraph (|p, |l...) It's simpler and clearer to use | apart.

Change: | now is an alias for (paragraph); the narrative syntax is clearer this way.

Change: p (as for "print" or "paragraph") is renamed to t (text); p" is renamed to t".

Change: passage is renamed to passage:.

Change: field prefix is changed from "~" to "+".

Fix: typo in comment of obey.

New: module used in order to hide the inner words.

2013-06-01

Fix: config variables were hidden.

Change: Removed ?link, ?l, ?+link and ?+l; they are not generic enough. A simple if in the narrative game can be used instead.

Change: link? renamed to linkable?.

Change: removed the hidden words that were used only once.

New: =link and =l" create visible links to the latest linked passage.

2013-06-09

Fix: =l" called link instead of =link.

Fix: The value of /command was wrong.

New: t\" and l\".

Change: text config values are stored with Gforth dynamic strings, not with svariable.

2013-06-19 (start of version A-01)

Change: The keywords management is rewritten: Keywords were dynamic strings in a fixed length table; now they are Forth words in a wordlist. This makes command completion possible, and makes the number of keywords per passage unlimited.

2013-06-20

Change: all "passage(s)" are changed to "scene(s)"; it's a better name.

New: separate wordlist for node keywords and common keywords.

2013-06-26 (start of version A-02)

Change: removed keywords/scene, forgotten from previous versions.

Change: all "scene(s)" are changed to "node(s)"; it's even a better name.

2013-06-28 (start of version A-03)

Change: better names for invisible link words; the low-level words can be used directly: (link) renamed to keyword, with alias k' (=link) renamed to +keyword , with alias +k'

Change: removed the words that create links with C-like \-escape sequences

Change: removed :keyword, forgotten from previous versions.

2013-07-05

New: Two tools words to forbid and allow spaces during the input.

2013-07-06

Fix: Now node saves the value of this on the return stack.

2013-07-07

Change: "common keywords" renamed to "global keywords".

New: First tools for global keywords.

Change: Better factoring for (+keyword).

New: -trailing_punctuation removes all chars, not only the last one.

2013-07-08

New: exit_this to force an early exit from the current node.

2013-07-09

First ideas for a new syntax for the narrative. The goal is the nodes to be created on the fly the first time they are used. No need to define their ids first.

Current syntax:

  node: node1-id
  node: node2-id

  node1-id :node
    node2-id k" keyword" +k" keyword"
    ;node

New syntax:

  node: node1-id
    ( definition )
    s" keyword" *-> node2-id
    s" keyword" <-*
    s" keyword" -> node2-id
    s" keyword" <-
    ;node

Options to create a link:

  s" keyword" -> node  \ invisible
  s" keyword" => node  \ visible

  s" keyword" -> node  \ invisible
  s" keyword" --> node  \ visible

  s" keyword" -> node  \ invisible xxx best
  s" keyword" *-> node  \ visible  xxx best

  s" keyword" => node  \ invisible
  s" keyword" ==> node  \ visible

  s" keyword" >> node  \ invisible
  s" keyword" >>> node  \ visible

Options to create an additional link:

  s" keyword" -<  \ invisible
  s" keyword" =<  \ visible

  s" keyword" <-  \ invisible
  s" keyword" <--  \ visible

  s" keyword" <-  \ invisible xxx best
  s" keyword" <-*  \ visible xxx best

  s" keyword" ->=  \ invisible
  s" keyword" =>=  \ visible

  s" keyword" <<
  s" keyword" >>+
  s" keyword" <+
  s" keyword" <==
  +" keyword"

  s" keyword" +>  \ invisible
  s" keyword" ++>  \ invisible

2013-07-10 (start of version A-04)

First changes to implement the new link syntax:

  s" keyword" -> node  \ invisible
  s" keyword" *-> node  \ visible
  s" keyword" <-  \ invisible, additional
  s" keyword" <-*  \ visible, additional

Nodes are created when defined or linked the first time.

Fix: the stack became unbalanced when a parsed node was already known.

Change: removed the subnodes stuff; secondary and secondary? are used instead.

The parsing syntax created new problems and obscure bugs. Besides, although node structures are created on the fly when nodes are defined or linked the first time, nodes id may be needed before linking or defining them, so the problem is not totally solved. It is much easier to return to a non-parsing syntax.

Draft ideas for a new syntax:

s" keyword" ~node ->  \ invisible
s" keyword" =->  \ invisible, synonym
s" keyword" ~node *->  \ visible
s" keyword" =*->  \ visible, synonym

s" keyword" ~node ->  \ invisible
s" keyword" +>  \ invisible, synonym
s" keyword" ~node *->  \ visible
s" keyword" *+>  \ visible, synonym

s" keyword" ~node ->  \ invisible
s" keyword" +->  \ invisible, synonym
s" keyword" ~node *->  \ visible
s" keyword" *+->  \ visible, synonym

\ good:
s" keyword" ~node ->  \ invisible
s" keyword" =>  \ invisible, synonym
s" keyword" ~node .->  \ visible
s" keyword" .=>  \ visible, synonym

\ parsing is not clear and is less versatile:
~node <" keyword"  \ invisible
<<" keyword"  \ invisible, synonym
~node <." keyword"  \ visible
<<." keyword"  \ visible, synonym

\ alternative
s" keyword" ~node ->  \ invisible
s" keyword" +>  \ invisible, synonym
s" keyword" ~node *->  \ visible
s" keyword" *+>  \ visible, synonym

\ alternative
s" keyword" ~node ->  \ invisible
s" keyword" <-  \ invisible, synonym
s" keyword" ~node .->  \ visible
s" keyword" <-.  \ visible, synonym

\ alternative
s" keyword" ~node ->  \ invisible
s" keyword" ->>  \ invisible, synonym
s" keyword" ~node .->  \ visible
s" keyword" .->>  \ visible, synonym

\ alternative
s" keyword" ~node ~  \ invisible
s" keyword" +~  \ invisible, synonym
s" keyword" ~node .~  \ visible
s" keyword" +.~  \ visible, synonym

\ alternative
s" keyword" ~node &  \ invisible
s" keyword" &&  \ invisible, synonym
s" keyword" ~node .&  \ visible
s" keyword" .&&  \ visible, synonym

\ good ( -> points to the next word, what looks strange,
\ but ^ seems a good option):
s" keyword" ~node ^  \ invisible
s" keyword" ^^  \ invisible, synonym
s" keyword" ~node .^  \ visible
s" keyword" .^^  \ visible, synonym

2013-07-10 (start of version A-05)

New syntax without parsing words (except for node:, that creates the node ids), same as version A-03.

New syntax for links:

s" keyword" ~node ^  \ invisible
s" keyword" ^^  \ invisible, synonym
s" keyword" ~node .^  \ visible
s" keyword" .^^  \ visible, synonym

Change: /node is a value, not a constant; this lets the programmer to extend the nodes structure.

Change: node ids are values, not constants; this lets the programmer to change the meaning of nodes any time.

New: "prompt" and "what?" are dynamic strings, not a string constants; /command is a calculation, not a constant; these changes let the programmer to customize the input line.

2013-07-10

New compact syntax instead of :global_keywords:

  local   global
  -----   ------
  ^       +^
  ^^      +^^

2013-07-12

Change: node renamed to ->.

2013-09-29

Change: (->) defined with :noname.

2014-02-13

New: unvisited?, moved from the game "La bleko de l' ŝargú".

Change: clearer comments separate the inner and application parts of the code; more code is moved to the application interface, in order to make the application more powerful. Other simple changes and corrections.

2014-02-17 (start of version A-06)

The narrative syntax needs a new rewrite, in order to make everything easier for the authors. The goal is to let keywords in the printed text, though the current Forth-like words will be provided as well.

In order to separate keywords from the surrounding text, parsing is needed. If the Forth interpreter is used, the link must start with a separated word.

A new free-text syntax was inspired by the word [", recently written for Galope,

An Asciidoctor-like link syntax, with the word <<, would look like this:

  |" Narrative inspired by one
  << ~original_narrative, book>> I just forgot. "|

A parsing word ~node-name, could be automatically created by node:.

But parsing a single character is easier and faster than parsing the final ">>". A simpler alternative could be:

  {" Narrative insipired by one
  { ~original_narrative book} I just forgot "}

Using the Forth interpreter creates a problem: how to know if the link has to be printed after an space or not?:

  {" Narrative insipired by one
  "{ ~original_narrative book}" I just forgot "}

An additional word could be defined for every case, e.g. the word "{, or use the flags and checks that already solved the same problem in Fendo's parser. But since the only markups are links, it seems easier to write a character-by-character parser, that searches for "{" and "}". This makes the syntax easier for the writer, because no extra spaces are needed. Also, synonyms could be specified:

  {" Narrative insipired by one
  "{ ~original_narrative book|publication|text}" I just forgot "}

Anyway, all kernel words that provide the manipulation of keywords have been renamed with clear names. The actual markup can be implemented with alias:

' enter_node alias ->
' node_keyword alias ^
' node_keyword+ alias ^^
' .node_keyword alias .^
' .node_keyword+ alias .^^
' global_keyword alias +^
' global_keyword+ alias +^^

Possible alternative:

' enter_node alias ~>
' node_keyword alias ^
' node_keyword+ alias ^+
' .node_keyword alias .^
' .node_keyword+ alias .^+
' global_keyword alias *^
' global_keyword+ alias *^+

New: node fields seconds+ and commands+; node and global counters of seconds and commands.

2014-02-18

Alternative markups. The better one seems to be the following one:

' enter_node alias }>
' node_keyword alias }
' node_keyword+ alias }+
' .node_keyword alias }.
' .node_keyword+ alias }.+
' global_keyword alias }*
' global_keyword+ alias }*+

false [if]

  \ ............................
  \ Usage example

  s" help" {help }*  \ global keyword
  s" sos" }*+  \ global extra keyword

  s" south" {celler } s" s" }+  \ two invisible keywords

  \ Text with two printed keywords to the same node:
  t" Her" s" home" {house }. t" is a little"
  s" house." }.+

  {node-id }>  \ go to a node

[then]

Change: .node_keywords and .global_keywods simplified

Problem: vocs and order crashes when is used in unclear conditions. After a lot of testing, it was clear the problem had nothing to do with Finto. The bug was found and fixed in .

New: First working version of ~", to provide the following syntax, with keywords in narrative text:

  ~" The lonely ~street street.
  You see your ~house house,
  the general ~store store,
  and the ~saloon saloon. "~

All markups will be provided as optional addons.

2014-02-19

New: Nodes use their own wordlist. Now node? simply uses search-list. New words to manage the wordlists order.

Fix: (node:) finally uses the lodge space thanks to the new word lodge-address-value written for the Galope's lodge module.

New: :node::

: :node:  ( "name" -- )
  \ Create a new node and start its definition.
  \ Usage examples:
  \   :node: ~node-id  ( definition )  ;node
  \   secondary :node: ~node-id  ( definition )  ;node
  node: execute-latest :node
  ;

Fix: :node is not immediate anymore.

Improvement: ~" now recognizes the markup for paragraphs, and the string used is configurable.

2014-02-20

Fix: A missing dup in enter_node caused the node code to be executed without its id on the stack.

New: First try to implement latest_node, a value to hold the id of the previous node, primary or secondary. So far previous_node holds the previous primary node only. A new approach is required about the tasks of primary and secondary nodes, because currently there's no way, beside using ordinary Forth variables, to know the latest secondary node.

Fix: After a lot of debugging, a bug was fixed: nodes were created with lodge-address-value, so they returned the absolute address of its data space in the lodge; the problem was the lodge address is variable because allocated. The solution was to create the nodes with lodge-value, so they return the lodge offset, and change the data interface of the node structure in order to convert the offset to the current absolute address.

Improvement: Now one word can be used to define all nodes, no matter if their ids have been created or not. This makes it easier to write the interactive fiction. :node and :node: are removed and combined into node:. The old node: is renamed node:

: node?  ( ca len -- xt true | false )
  \ Is a string a node-id?
  nodes_wid search-wordlist
  ;
: name>node  ( "node-id" -- node )
  \ Parse a node-id and return its value. 
  \ If the parsed node-id has not been created before, create it.
  >in @ >r parse-name r> >in !
  node? if  execute  else  node execute-latest  then
  ;
: node:  ( "node-id" -- )
  \ Start the definition of a node.
  \ If the node-id has not been created before, create it.
  \ Usage examples:
  \   node: ~node-id  ( definition )  ;node
  \   secondary node: ~node-id  ( definition )  ;node
  name>node dup :noname  ( node node xt colon-sys ) colon-sys>r
  [:node] ( node )  postpone literal  secondary? @
  if    postpone (:secondary_node) secondary? off
  else  postpone (:primary_node)
  then  r>colon-sys  ( colon-sys )
  ;

2014-02-21

First tries for saving and restoring a game:

secondary node: ~save
  \ xxx todo choose filename
  lodge @ /lodge @ s" ~/forth/lpda/lpda.data" unslurp-file
  | t" [Saved.]"
  ;node
node: ~load
  \ xxx todo choose filename
  s" ~/forth/lpda/lpda.data" slurp-file lodge @ swap move
  ~" | [Loaded. Press any key to continue were you saved.] "~
  key drop  print_page  previous_node ~>
  ;node

But there are two problems:

First, the execution tokens of the nodes, stored in the lodge, are absolute! That makes it impossible to restore a game in a different Gforth session. A lookup table will solve this.

Second, in order to continue the game, the node that was the current one when the save command was invoqued, must be executed again after the loading. This means some things may be different, depending on what the node actually does at the start. There's now solution for this.

2014-02-22

New: Execution tokens of the nodes are stored in a table. This table has been implemented as a lodge. The Galope's "lodge-colon" module makes it possible to create several lodge buffers.

2014-02-24

Change: The code that restores saved sessions has been rewritten. Now the sessions list is not a text file that must be read line by line, but a Forth source file ready to be included; it creates the session filenames keywords.

2014-02-25

New: The code to save and restore sessions is finished but not fully tested. Only two interface words are required by the user application: load_session and save_session.

2014-05-22

Change: The string parsed by ~" ends with a double quote, not with "" "~ "".

Fix: The this value failed in nested nodes, because it was updated at run-time, at the end of every node. The solution was to convert it to an immediate word that compiles the id of the node being compiled. A new variable, [node] was created to hold the id (;node) and the words that call it had to be modified.

2014-07-08

Same little changes in the source format.