1
1
Fork 0
mirror of https://github.com/tonydamage/nux-env.git synced 2025-12-11 13:24:28 +01:00

nuxsh: Added custom syntax to bash.

Signed-off-by: Tony Tkacik <tonydamage@gmail.com>
This commit is contained in:
Tony Tkáčik 2017-07-16 18:37:42 +02:00
parent acefb64cdc
commit eef3628269
3 changed files with 352 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
cache

142
inc/nux/dsl.inc.sh Normal file
View file

@ -0,0 +1,142 @@
#!/usr/bin/env bash
## nulang - NUX Custom DSL Support Library
##
## # Language Definition
##
## Language is defined in terms of BASH REGEX matches and functions that process
## or execute particular match.
NUDSL_CACHE_SUFFIX=".nux.dsl.sh"
nux.dsl.eval() {
$nudsl_eval "$@"
}
nux.dsl.env() {
.process.highlight() {
echo "$line";
}
.match._unmatched.highlight() {
echo "${_gen_highlight_unmatched}$line${nc_end}"
}
.gen.parser._unmatched.process() {
nux.exec.or .match._unmatched.$action .process.$action
}
.highlight() {
nux.dsl.eval _gen_highlight_$1='$nc_'$2
}
.match() {
local type=$1;
local pattern=$2;
shift; shift;
i=0;
local parse_body="";
nux.dsl.eval _gen_parser_types='"$_gen_parser_types '$type'"'
nux.dsl.eval _gen_parser_pattern_$type="'"$pattern"'"
nux.dsl.eval """.gen.parser.$type.process() {
$(
for group in "$@"; do
let i=$i+1;
if [ "$group" != "-" ]; then
echo local ${group}='${BASH_REMATCH['$i']}'
fi
done
)
nux.exec.or .match.$type.\$action .process.\$action
}
"""
nux.dsl.eval """.match.$type.highlight() {
$(
for group in "$@"; do
let i=$i+1;
if [ "$group" != "-" ]; then
echo ' echo -n "${_gen_highlight_'$group'}$'$group'${nc_end}"'
fi
done
)
echo;
}
"""
}
}
nudsl_eval=eval
nux.dsl.process() {
local action=$1;
local language=$2;
local file=$3;
(
nux.dsl.env
$language
cat "$file" | nux.dsl.process0 $action)
}
nux.dsl.exec() {
local language="$1";
local file="$2";
local cached="${3:-$file${NUDSL_CACHE_SUFFIX}}";
if nux.dsl.plan "$language" "$file" "$cached"; then
source "$cached";
fi
}
nux.dsl.plan.file() {
local language="$1"
local file="$2";
echo "$file${NUDSL_CACHE_SUFFIX}";
}
nux.dsl.plan() {
local language="$1";
local file="$2";
local cached="${3:-$file${NUDSL_CACHE_SUFFIX}}";
if [ "$file" -ot "$cached" ]; then
nux.log debug No need to recompile.
return;
fi
nux.log debug Needs regeneration, creating new version.
local dirname=$(dirname "$cached")
mkdir -p "$dirname";
local execution_plan=$(mktemp "$dirname/.nux.dsl.XXXX")
if (nux.dsl.process plan "$language" "$file" > "$execution_plan") ; then
mv -f "$execution_plan" "$cached";
else
echo "Plan could not be generated. See errors."
rm "$execution_plan"
return -1;
fi
}
nux.dsl.process.fail() {
process_failed=true
echo "$linenum:$@" >&2
}
nux.dsl.process0() {
local _gen_parser_pattern__unmatched='(.*)';
local patterns="$_gen_parser_types _unmatched";
local linenum=0;
while IFS= read -r line ;
do
let linenum=$linenum+1
for t in $patterns; do
local pattern=_gen_parser_pattern_$t
if [[ "$line" =~ ${!pattern} ]]; then
.gen.parser.$t.process
break;
fi
done
if [ -n "$process_failed" ]; then
return -1;
fi
done;
}

209
inc/nux/nuxsh.inc.sh Normal file
View file

@ -0,0 +1,209 @@
nux.use nux/dsl
nux.nuxsh.language.def() {
local identifier='[^ ;{}=()$]+'
local comment='(( *)(#.*))?'
local whitespace="([ ]*)"
local uarg="([^ #\{\}\"'\"'\"';]+)";
local sarg="('\"'\"'[^'\"'\"']+'\"'\"')";
local darg='("[^"]*")';
local args="((($uarg|$darg|$sarg) *)*)";
local prefixed_id="([^ :]*:)?($identifier)"
.match.line() {
local type="$1";
local match="^( *)$2$comment$";
shift;shift;
.match "$type" "$match" indent "$@" - indent_comment comment;
}
.match.line comment ''
.match.line rule "(@)([^ ]+)( +)$args?( *);?"\
syntaxM rule indent2 args - - - - - indent3 syntax2
.match.line namespace_block_start "(@)(namespace)(( +)$uarg)( *)(\{)" \
syntaxM keyword - indent2 identifier indent3 syntax3
#.match.line namespace_start "@(namespace)( +)$uarg( *)(\{)" \
# namespace indent2 args indent3 syntax
.match.line block_end '(\})' \
syntax
.match.line if_start "(if)( +)$prefixed_id( +)$args?( *)(\{)" \
keyword indent2 prefix identifier indent3 args - - - - - indent4 syntax3
.match.line function_start "((function)( +))($identifier)((\()|( *))(($identifier,? *)*)(\))?( *)(\{)" \
- keyword indent2 identifier - syntax indent3 args - syntax2 indent4 syntax3
.match.line block_start "($identifier)(( +)$args)?( *)(\{)" \
identifier - indent2 args - - - - - indent3 syntax3
.match.line statement "$prefixed_id(( +)$args)?( *)(;?)"\
prefix identifier - indent2 args - - - - - indent3 syntax2
#.match.line variable "([^ ]+=)$args( *)(;?)"\
# variable args - - - - - indent3 syntax
.highlight rule cyan
.highlight syntaxM cyan
.highlight prefix cyan
.highlight identifier green
.highlight keyword blue
.highlight args yellow
.highlight comment magenta
.highlight unmatched red
.highlight syntax white
.highlight syntax2 white
.highlight syntax3 white
blocktrac_root="#blocktrac_root"
_block_type[${#_block_type[@]}]="$blocktrac_root"
function .block.get {
echo ${_block_type[${#_block_type[@]}-1]}
}
function .block.pop {
unset _block_type[${#_block_type[@]}-1]
}
function .block.push {
_block_type[${#_block_type[@]}]="$1"
}
.match.block_start.plan() {
.block.push $identifier;
nux.exec.or .block.$identifier.start.plan .block.start.plan
}
.match.block_end.plan() {
local identifier=$(.block.get)
if [ "$identifier" == "$blocktrac_root" ]; then
nux.dsl.process.fail "unnecessary block end '$line' "
return -1;
fi
nux.exec.or .block.$identifier.end.plan .block.end.plan
.block.pop;
}
.action.alias() {
local alias=$1; shift;
echo "# alias: $alias $@";
eval "_alias_$alias='$@'";
}
.action.prefix() {
echo "# prefix: $1 $2"
eval "_import_prefix_$1='$2'";
}
.identifier() {
if [ -n "$prefix" ]; then
local var=_import_prefix_${prefix%:}
local prepend=${!var};
if [ -z "$prepend" ] ; then
nudsl.process.fail "undefined prefix: $prefix";
fi
echo "$prepend$identifier"
else
echo "$identifier"
fi
}
.match.statement.plan() {
echo "${indent}$(.identifier) ${args}"
}
.match.rule.plan() {
eval ".action.${rule//:/.} $args";
}
.process.plan() {
echo "$line";
}
.match.if_start.plan() {
.block.push lang.if;
echo "${indent}${keyword} $(.identifier) ${args} ; then"
}
.block.lang.if.end.plan() {
echo "${indent}fi";
}
.match.namespace_block_start.plan() {
.block.push rule.namespace;
echo "# namespace $identifier"
_namespace="$identifier"
_import_prefix_="$identifier"
}
.block.rule.namespace.end.plan() {
_namespace=""
_import_prefix_=""
echo "#namespace end"
}
.match.function_start.plan() {
.block.push function
case $identifier in
.*) ;;
:*) identifier="$_namespace${identifier#:}"
esac;
echo "${indent}$identifier() {";
for arg in ${args//,/ }; do
echo "${indent} local $arg="'"$1"'";shift;"
done
}
.block.start.plan() {
case $identifier in
function) echo "$line";;
*"()") echo "$line";;
*) nudsl.process.fail Invalid block syntax: "'$identifier' '$line'";
esac;
}
.block.end.plan() {
.process.plan;
}
.do.function.prefix() {
echo "${indent}function $1$args {"
}
.action.block.rewrite.function.prefix() {
echo "# block:rewrite:function:prefix $@"
eval """.block.$1.start.plan() {
.do.function.prefix "$2"
}
"""
}
.action.block.rewrite.call() {
echo "# block:rewrite:block:call $@"
eval """.block.$1.start.plan() {
echo \"\${indent}\"'${2}'\" \$args\"'${3}'
}
.block.$1.end.plan() {
echo \"\${indent}\"'${4}'
}
"""
}
}
function nux.nuxsh.use {
local file="$1";
local cached="$2";
nux.dsl.exec nux.nuxsh.language.def "$file" "$cached"
}