parent
2c4693ca23
commit
40d5cd5e6b
@ -0,0 +1 @@ |
|||||||
|
eval "$(lorri direnv)" |
@ -0,0 +1,81 @@ |
|||||||
|
# craftlang |
||||||
|
|
||||||
|
Craftlang (`.craft` files) is an expression language for crafting |
||||||
|
recipies and block arrangement in the popular free software block |
||||||
|
building game [Minetest](https://minetest.org). This tool is capable |
||||||
|
of parsing `.craft` files and generating image outputs. |
||||||
|
|
||||||
|
## Usage |
||||||
|
|
||||||
|
This tool is written in [Raku](https://raku.org) and such you need to have `rakudo` |
||||||
|
installed on your system. Additionally you need to make your minetest |
||||||
|
assets tree and environment files available via ennvironment |
||||||
|
variables. |
||||||
|
|
||||||
|
```console |
||||||
|
$ env MTCRAFT_ENV_PATH=/path/to/environments \ |
||||||
|
MINETEST_ASSETS=/path/to/minetest/assets/root \ |
||||||
|
craftlangc ./path-to-recipe.mtcraft |
||||||
|
``` |
||||||
|
|
||||||
|
|
||||||
|
## Language overview |
||||||
|
|
||||||
|
Craftlang is a domain-specific language for expressing recipes, based |
||||||
|
on a small set of keywords, with associated payloads. A payload is |
||||||
|
either a primitive or scope. Scopes use two-space indentation to |
||||||
|
handle end-of-scope separation. |
||||||
|
|
||||||
|
### Example |
||||||
|
|
||||||
|
The following example should be enough to explain the basic structure |
||||||
|
of primary mtcraft files. |
||||||
|
|
||||||
|
```mtcraft |
||||||
|
ENV alloys |
||||||
|
INPUTS |
||||||
|
copper_ingot 7 |
||||||
|
tin_ingot 1 |
||||||
|
OUTPUTS |
||||||
|
bronze_ingot 8 |
||||||
|
``` |
||||||
|
|
||||||
|
The `ENV` keyword loads an environment file. Environment files add a |
||||||
|
set of metadata which is added to a generated output, and also |
||||||
|
provides a way to specify how other keywords are handled. |
||||||
|
|
||||||
|
The `INPUTS` and `OUTPUTS` keywords open scopes. A scope is a set of |
||||||
|
statements that is specific to the loaded environment but must be one |
||||||
|
of the following: `key-value`, `marker` or `keyword`. |
||||||
|
|
||||||
|
The example above contains three `key-value` statements. A `marker` |
||||||
|
statement does not contain additional data and instead provides a |
||||||
|
boolean flag to the environment. A `keyword` statement provides a way |
||||||
|
to have nested `ENV`, `INPUTS`, and `OUTPUTS` scopes. |
||||||
|
|
||||||
|
|
||||||
|
### Environment files |
||||||
|
|
||||||
|
The environment provided via the `ENV` keyword must be present in the |
||||||
|
`MTCRAFT_ENV_PATH`. An environment specifies the input-output |
||||||
|
mappings for `INPUTS` and `OUTPUTS` scopes. |
||||||
|
|
||||||
|
Required keywords for an ENV file are `BG`, `INPUTS`, `MODE`, and |
||||||
|
`OUTPUTS`. The `SPACING` keyword is permitted outside of the `INPUTS` |
||||||
|
and `OUTPUTS` blocks to be applied to both simultaniously. The `MODE` |
||||||
|
keyword selects the general output mode. Currently accepted modes are |
||||||
|
`crafting` and `arrangement` |
||||||
|
|
||||||
|
```mtcraftenv |
||||||
|
BG alloy_furnace |
||||||
|
MODE crafting |
||||||
|
SPACING 15 15 |
||||||
|
INPUTS |
||||||
|
ANCHOR 125 125 |
||||||
|
COUNT 2 1 |
||||||
|
OUTPUTS |
||||||
|
ANCHOR 525 125 |
||||||
|
COUNT 1 1 |
||||||
|
``` |
||||||
|
|
||||||
|
The default fill-mode is Left-to-right-top-to-bottom. |
@ -0,0 +1,64 @@ |
|||||||
|
#!/usr/bin/env raku |
||||||
|
|
||||||
|
grammar FileGrammar { |
||||||
|
# The top-level file is a list of statements |
||||||
|
token TOP { ^ <line>* %% \v* $ } |
||||||
|
|
||||||
|
# A line is either a list of keyword-payload or scope |
||||||
|
token line { <statement> | <scope> } |
||||||
|
|
||||||
|
# A statement is a simple keyword-payload mapping |
||||||
|
token statement { <keyword> \s <payload> } |
||||||
|
|
||||||
|
# The set of simple key-value keywords |
||||||
|
token keyword { < ANCHOR BG COUNT ENV MODE SPACING > } |
||||||
|
|
||||||
|
# A simple payload |
||||||
|
token payload { \S+ %% \h* } |
||||||
|
|
||||||
|
# A scope is a scope-word + list of scope-lines |
||||||
|
token scope { <scope-word> <scope-line>* } |
||||||
|
|
||||||
|
# The set of scope-opening keywords |
||||||
|
token scope-word { < INPUTS OUTPUTS > } |
||||||
|
|
||||||
|
# A scope line is either a simple payload or a full statement |
||||||
|
token scope-line { \v+ \h \h [ <statement> | <payload> ] } |
||||||
|
} |
||||||
|
|
||||||
|
class Actions { |
||||||
|
method TOP($/) { |
||||||
|
make $<line>>>.made |
||||||
|
} |
||||||
|
|
||||||
|
method line($/) { |
||||||
|
make .made with $<statement> || $<scope>; |
||||||
|
} |
||||||
|
|
||||||
|
method statement($/) { |
||||||
|
make { Keyword => $<keyword>.made, Payload => $<payload>.made } |
||||||
|
} |
||||||
|
|
||||||
|
method scope($/) { |
||||||
|
make { Keyword => $<scope-word>.made, Payload => $<scope-line>>>.made } |
||||||
|
} |
||||||
|
|
||||||
|
method scope-line($/) { |
||||||
|
make $<statement> ?? $<statement>.made !! { Payload => $<payload>.made } |
||||||
|
} |
||||||
|
|
||||||
|
method keyword($/) { make ~$/ } |
||||||
|
method payload($/) { make ~$/ } |
||||||
|
method scope-word($/) { make ~$/ } |
||||||
|
} |
||||||
|
|
||||||
|
sub MAIN($file, Bool :$verbose) { |
||||||
|
my $input = slurp $file; |
||||||
|
say "Parsing input: $input" if $verbose; |
||||||
|
|
||||||
|
my @parse = FileGrammar.parse($input, actions => Actions.new).made; |
||||||
|
|
||||||
|
for @parse -> %line { |
||||||
|
say %line |
||||||
|
} |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
import <shells/raku> {} |
@ -0,0 +1,6 @@ |
|||||||
|
ENV alloys |
||||||
|
INPUTS |
||||||
|
copper_ingot 7 |
||||||
|
tin_ingot 1 |
||||||
|
OUTPUTS |
||||||
|
bronze_ingot 8 |
Loading…
Reference in new issue