@mog13/splain
v2.1.1
Published
Small transformer library to create more dynamic and interesting language/sentances
Downloads
82
Readme
Splain
Small parser to create more interesting language/sentances
See it in action:
https://mog13.github.io/Splain/
https://codepen.io/collection/DKmzKY/
See a tutorial at:
https://mog13.github.io/Splain/loadingTutorial.html
Installation
you can install:
- via npm
npm install @mog13/splain
- copy splain.ts into your source
and then import it into your project via:
- a script tag:
<script src="./link_to_splain.js"/>
- require
require("@mog13/splain")
- import
import Splain from "@mog13/splain"
Overview
Splain is a template processor that helps you build more varied text and dynamic language. To do this you provide splain with a dictionary of options and then reference them in templates. These templates are made up of tokens which are then resolved recursively.
Using a blend of different tokens and template allows you to quickly build up dynamic text which can be as simple as random selection or a rich custom response informed by runtime variables.
Features
Templates
At its simplest it can be utilized to reference a pool of words to select from. For example you can imagine a use case for greeting a customer. a hardcoded message may become stale so
instead we can use splain to provide some variety. We do this by populating a dictionary entry keyed greeting
and reference it in a template that we then process. When processing Splain
will simply swap the token for something in the associated entry.
Splain.addEntry({greeting: ["hello", "howdy", "hey", "hi","welcome","greetings"]});
let greetingMessage = Splain.process("{{greeting}}! have a great day!");
console.log(greetingMessage);
Every time the Process function is run the templates will be recompiled giving a new output. For example we could create a dynamic loading screen.
Splain.addEntry({loadingMessage:["collecting bits", "compiling bytes", "reticulating splines", "calculating normals", "rebooting router"]});
function showLoadingMessage() {
let loadingMessage = Splain.process("loading: {{loadingMessage}}");
console.log(loadingMessage);
}
setInterval(showLoadingMessage,5000);
Splain templates can contain many tokens. so for example {{token1 token2}}
. Strings to process can also contain many templates e.g. {{token1}} not a token {{token2}}
.
Splain dictionaries are just JSON objects and can be layered allowing you to create categories or collect similar entries together.
Entries can also contain templates themselves to create a more complex structure. doing this can mean that each additional entry can result in exponentially more possible outcomes!
Using this information we can start to build up a more sophisticated use case, like a weather app.
Splain.addEntry({
weather: {
warm: ["really {{adj.temp.warm}}", "{{adverbs.very adj.temp.warm}}", "hot hot hot", "hotter than the sun","like living in desert","hotter than an oven","like an inferno", "tropical"],
cold: ["really {{adj.temp.cold}}", "{{adverbs.very adj.temp.cold}}", "arctic", "pretty chilly", "too cold to bother", "cold enough to freeze your eyes shut", "warmer in the fridge"],
comments: {
intro: ["Its going to be", "Coming up itl be", "Later itl be", "Today im predicting itl be", "Today will be","The weather will be", "Our computers predict itl be"],
itemReminder: ["So pack", "So don't forget", "Better bring"],
warm: ["{{weather.comments.itemReminder}} the sunscreen", "So stay hydrated", "Good luck", "Sounds like its time for a barbecue", "I hope you like it hot"],
cold: ["{{weather.comments.itemReminder}} {{pronoun}} sweater", "{{weather.comments.itemReminder}} {{pronoun}} hoody", "{{weather.comments.itemReminder}} {{pronoun}} coat", "brrrrr!", "Maybe we should migrate for the winter?", "Time to book that tropical holiday"]
},
report: {
warm: ["{{weather.warm}}. {{weather.comments.warm?2}}"],
cold: ["{{weather.cold}}. {{weather.comments.cold?2}}"]
}
},
pronoun: ["a", "your"],
adj: {
temp: {
cold: ["cold", "freezing", "icy", "chilly", "frigid", "frosty", "polar", "bitterly", "wintry","snowy"],
warm: ["hot", "boiling", "sweltering", "balmy", "humid","sizzling","sweltering","warm","sunny","summery"]
},
},
adverbs: {
very: ["very", "exceedingly", "awfully", "greatly", "decidedly", "extraordinarily","incredibly","unusually","surprisingly", "excessively", "quite","uncommonly"]
}
}
);
function weatherReport(degreesC) {
let report = "{{weather.comments.intro weather.report.cold}}";
if(degreesC > 20) report = "{{weather.comments.intro weather.report.warm}}";
return Splain.process(report);
}
console.log(weatherReport(15));
console.log(weatherReport(30));
Weighting In some instances you may want to provide weights to an entry. This effectively loads the dictionary with multiple copies of the entry to bias towards it. For example we could re-purpose our loading example to show a easter egg message once in a while by weighting our normal messages much higher.
Splain.addEntry({loadingMessage:[{value: "constructing models", weight: 100},{value: "calculating indices", weight: 100},"stealing credit card information"]});
function showLoadingMessage() {
let loadingMessage = Splain.process("loading: {{loadingMessage}}");
console.log(loadingMessage);
}
setInterval(showLoadingMessage,5000);
note adding a weighting is optional per entry, as we can see in the example not all entries require a weight and you are free to mix and match
Fixed resolution
Sometimes you may want to fix a tokens output in a given process, i.e if that token is called again use the previous result. you can use the :: operator to make it fixed resolution.
Splain.addEntry({language:["JS","Javascript","ECMAscript"]});
console.log(Splain.process("{{::language}} is the best language, you can use {{::language}} to do anything!"));
Contexts
It may not make sense to just pick a random entry and you may want Splain to make a smarter choice based on preceding selections. You can achieve this with contexts. If splain selects an entry with a context then in future template executions it will try and select from entries with the same context. If none are discovered then it will revert to the original pool of options.
Splain.addEntry({colors:[{value:"blue", context:"blue"},{value:"green", context:"green"},{value:"turquoise", context:"blue"}]});
Splain.addEntry({environments:[{value:"sea", context:"blue"},{value:"grass", context:"green"},{value:"sky", context:"blue"},{value:"forest", context:"green"}]});
console.log(Splain.process("wow look at those lovely {{colors environments}}"));
//note the context is at the process level so it will persist over different templates
console.log(Splain.process("wow look at those lovely {{colors}} {{environments}}"));
Variables
You can make a variable template using the ##token. This will use a variables object that you can pass into the process function to resolve the token.
Splain.addEntry({late:["almost time for bed!","the day is almost over!"],early:["the day has just begun!"]});
let getTimePhrase = function() {
return new Date().getHours()<12? "{{early}}":"{{late}}";
};
let bestLibrary = "Splain";
console.log(Splain.process("you should try {{##library}}",{library:bestLibrary}));
console.log(Splain.process("we are on month {{##currentMonth}}/12",{currentMonth:()=>{return new Date().getMonth()+1}}));
console.log(Splain.process("look at the time, it's {{##currentTime}}! {{##timePhrase}}",{currentTime:()=>{return new Date().getHours() + ":" +new Date().getMinutes()},timePhrase:getTimePhrase()}));
Operators
You can use operators to act on other tokens.
literal
You can specify a word should be displayed as is by surrounding it with backticks (`). You could just place the word outside the template and it wouldn't be touched, however sometimes you may want to apply some of Splains other features to a literal without it necessarily being translated through a dictionary.
or
A | token represents an either or so will select either the proceeding or preceding token.
console.log(Splain.process("hello {{`world`|`internet`}}"))
conditional
A ? token provides a 1 in X chance of being rendered. X can be specified by providing the number after the ?. If no number is specified it is defaulted to 2.
console.log(Splain.process("goodbye!{{`and thanks for all the fish`?42}}"))
So in the above roughly one in every 42 compiles there will be a hitchhikers guide to the galaxy reference.
Configuration
Splain can be configured with several options via its config object: Splain.config.configure(property, value);
The following properties can be changed:
keepTemplateOnUnmatched (default: true)
This boolean controls what is returned on the event a token isn't matched. If true it will return the name of the token. If false it will return null.
fixedResolutionToken (default: "::")
This is the token used to identify or define a fixed resolution token
variableResolutionToken (default: "##")
This is the token used to identify or define a variable resolution token
templateTokens
default: {opening: "{{", closing: "}}"}
This defines how a template is identifies
Developing
Pull requests welcome (with relevant tests).
npm install
to install
npm build
to bundle with webpack;
npm test
to run all the tests. (npm lint-fix
will fix minor eslint issues such as spacing)
Feel free to raise bugs/issues. Issues containing supporting code or tests to prove the bug would be very appreciated
Testing
Testing is done via Jest and Jasmine.
Tests can be run with npm test
or simply jest
Credits and thanks
- Luke Rogers For all his contributions, ideas and implementing the lions share of V2.
- The many contributors from Hacktoberfest for helping and joining in