diff --git a/prototypes/craftlang/.envrc b/prototypes/craftlang/.envrc new file mode 100644 index 00000000000..051d09d292a --- /dev/null +++ b/prototypes/craftlang/.envrc @@ -0,0 +1 @@ +eval "$(lorri direnv)" diff --git a/prototypes/craftlang/.projectile b/prototypes/craftlang/.projectile new file mode 100644 index 00000000000..e69de29bb2d diff --git a/prototypes/craftlang/README.md b/prototypes/craftlang/README.md new file mode 100644 index 00000000000..b4fd82a31b1 --- /dev/null +++ b/prototypes/craftlang/README.md @@ -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. diff --git a/prototypes/craftlang/craftlangc b/prototypes/craftlang/craftlangc new file mode 100755 index 00000000000..240f46a278e --- /dev/null +++ b/prototypes/craftlang/craftlangc @@ -0,0 +1,64 @@ +#!/usr/bin/env raku + +grammar FileGrammar { + # The top-level file is a list of statements + token TOP { ^ * %% \v* $ } + + # A line is either a list of keyword-payload or scope + token line { | } + + # A statement is a simple keyword-payload mapping + token statement { \s } + + # 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 { * } + + # 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 [ | ] } +} + +class Actions { + method TOP($/) { + make $>>.made + } + + method line($/) { + make .made with $ || $; + } + + method statement($/) { + make { Keyword => $.made, Payload => $.made } + } + + method scope($/) { + make { Keyword => $.made, Payload => $>>.made } + } + + method scope-line($/) { + make $ ?? $.made !! { 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 + } +} diff --git a/prototypes/craftlang/shell.nix b/prototypes/craftlang/shell.nix new file mode 100644 index 00000000000..9399d681340 --- /dev/null +++ b/prototypes/craftlang/shell.nix @@ -0,0 +1 @@ +import {} diff --git a/prototypes/craftlang/test.cl b/prototypes/craftlang/test.cl new file mode 100644 index 00000000000..438baa2ed1c --- /dev/null +++ b/prototypes/craftlang/test.cl @@ -0,0 +1,6 @@ +ENV alloys +INPUTS + copper_ingot 7 + tin_ingot 1 +OUTPUTS + bronze_ingot 8 \ No newline at end of file