dhowe / ritajs-v2

RiTa: generative language tools
https://rednoise.org/rita
GNU General Public License v3.0
104 stars 11 forks source link

Grammars in Twine #140

Open dhowe opened 3 years ago

dhowe commented 3 years ago

I'd like to support the following two types of grammar cases (with and without closing tag). Can you see if this is possible?

<<rg $rules >>

<<rg>>
{
  rules
}
<</rg>>
Real-John-Cheung commented 3 years ago

I think this <<rg $rules>> <<rg>>{}<</rg>> is not possible at least in sugarCube... coz a Macro is either self-closing or have closing tag, and there can't be two Marcos of the same name

it is possible to have something like this <<rg $rules>> <<rg {"start":"a"}>> //must be a valid json object without line break //or <<rg '{ start: "a" }'>> //can be json or js object and have line-breaker, but must pass in as a string

dhowe commented 3 years ago

ok, can you include (either here or in a notebook) the simplest possible code for a <<rg>> macro as below:

<<rg>>{start: $rule1, ... }<</rg>>

<<rg>>$rules<</rg>>

and also with an optional start rule:

<<rg 'optionalStartRuleName'>>$rules<</rg>>

where rules is either a literal JS object, or a previously declared twine variable no need for JSON unless it is an external .json file (not sure how this works in twine)

note: the only added benefit of doing this, over what what he have now, is that we can pass State.variables to grammar.expand automatically

Real-John-Cheung commented 3 years ago

https://observablehq.com/@real-john-cheung/riscript-and-rigrammar-in-twine rules can be a literal JS object, a json object or a twine variable

setup.JSLoaded = false;
setup.lockID = LoadScreen.lock();
function parseObject (obj) {
  return Function('"use strict";return (' + obj + ')')();
}
importScripts("https://unpkg.com/rita").then(function () {
  Macro.add('rg',{
   skipArgs: false,
   tags: null,
   handler: function () {
     let s = this.payload[0].contents.trim();
     let o;
     try {
        o = JSON.parse(s);
     } catch(e) {
     }
     if (!o) {
        try {
           o = parseObject(s);
        } catch {
        }
     }
     if (!o && /^\$[A-Za-z0-9$_]+$/.test(s)){
     s = s.replace('$','');
     o = State.variables[s] ? State.variables[s] : undefined;
     }
     if (!o) throw Error("fail to parse grammar, " + s + " is not a vaild object.");
     $(this.output).wiki(RiTa.grammar(o).expand(this.args[0],State.variables));
   }
  });
  setup.JSLoaded = true;
  Engine.play(passage(), true);
  LoadScreen.unlock(setup.lockID);
}).catch(function (err) {
  alert(err);
});
dhowe commented 3 years ago

the grammar() function already accepts json and js objects, no? why parse them separately ?

Real-John-Cheung commented 3 years ago

because what inside Macro.playload.contents is string, so it need to be parsed into object

grammar() actually can take in JSON string but literal jsobject string need to parsed first

dhowe commented 3 years ago

so we don't need JSON.parse in there, correct?

Real-John-Cheung commented 3 years ago

We don't need it (i.e. we can code another (I think will be more complex) version that do the same without it) but maybe we should keep the JSON.parse() because we don't want to pass a random(unchecked) string into grammar(), and with it we can make sure that the type of the parameter pass in grammar() is always object.

dhowe commented 3 years ago

sounds good, reassigning to myself