#!/usr/bin/env raku grammar FileGrammar { # The top-level file is a list of lines token TOP { ^ * %% \v* $ } # A line is either a statement 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 is 2-space indented and either a statement or payload 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 load_env($path, $env, $verbose) { unless "$path/$env.craftenv".IO.e { say "Required environment '$env' not found in search path '$path'!"; exit 2; } my $input = slurp "$path/$env.craftenv"; say "Parsing input: $input" if $verbose; my @parsed = FileGrammar.parse($input, actions => Actions.new).made; @parsed.map({ if $_ ~~ Str:D { $_ => $_ } else { $_ => $_.map({ $_ => $_}).Hash } }).Hash } sub MAIN($file, Str :$env="/dev/null", Str :$assets, Bool :$verbose) { # Load and parse the main input file my $input = slurp $file; say "Parsing input: $input" if $verbose; my @parsed = FileGrammar.parse($input, actions => Actions.new).made; my %tree = @parsed.map({ $_ => $_ ~~ Str:D ?? $_ !! $_.map({ $_ }) }).Hash; # Load and parse the environment tree from the loaded input my %env = load_env($env, %tree, $verbose); # Print both for good measure %tree.say; %env.say; }