-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathknight
More file actions
executable file
·148 lines (124 loc) · 6.08 KB
/
knight
File metadata and controls
executable file
·148 lines (124 loc) · 6.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/bin/sh
# This is Knight implemented in POSIX-compliant sh. Everything in this program
# (and the files it includes) are 100%-POSIX compliant, and are guaranteed to
# work on all POSIX-compliant shells. (Including KSH, assuming you have the
# newest one installed---older ones have some bugs!)
################################################################################
# #
# Background #
# #
################################################################################
## Naming scheme for variables
# As POSIX-compliant sh doesn't have local vars, I use the following scheme:
#
# READ_ONLY -- these variables are declared `readonly`
# Global_Variables -- these variables are intended to be read cross-function
# local_variable -- only local to the current function.
#
# Most local variables can be assumed to be clobbered when calling functions.
#
# Of note is the variable `Reply` which stores the "return value" from functions
# that need to return something.
##
## Value representation
# As POSIX-compliant sh only allows for string variables, this program uses the
# very first character of a "value" to distinguish its type. Values are in the
# following (regex) formats:
# i-?\d+ - integers
# s.* - strings
# [TFN] - TRUE, FALSE, or NULL.
# v[a-z_][a-z0-9_] - variable names.
# a(\d+)(${ARY_SEP}ele)* - arrays, `\1`` is the array length, and `\2`+ are
# the elements. (elements don't contain `$ARY_SEP`.)
# A(\d+) - array reference. Because arrays cannot contain
# `$ARY_SEP`, nested arrays use array references. To
# get the original back, you `eval` the reference.
# f(.)(${FN_SEP}ele)* - functions, `\1`` is the name, `\2`+ are arguments.
# (arguments don't contain `$FN_SEP`)
# F(\d+) - function reference. Just like array references,
# except for functions.
##
################################################################################
# #
# Ensure POSIX Compliance #
# #
################################################################################
# Convey to external utilities and the shell we want POSIX-compliance
export POSIXLY_CORRECT=1
# If we're using bash, make sure it's POSIX compliant as well.
[ -n "$BASH_VERSION" ] && set -o posix
# Ditto, for zsh
[ -n "$ZSH_VERSION" ] && emulate sh
################################################################################
# #
# Setup #
# #
################################################################################
set -o nounset # undefined variables raise errors, not return empty strings
set -o noglob # disable globbing (useful in variable expansions)
## Separators to separate elements; these don't exist in the Knight encoding set
# so they're safe to use. (I picked the ascii separators for fun, but it could
# be anything that's not in Knight and not considered whitespace.)
readonly FN_SEP="$(printf \\036)" # 036 is "Record separator"
readonly ARY_SEP="$(printf \\037)" # 037 is "Unit separator"
readonly EXEC_SEP="$(printf \\034)" # 034 is "File separator"
## Ensure `a-z`, `0-9`, etc when parsing are POSIX compliant. Without this, it's
# actually entirely implementation defined what `a-z` means.
readonly LC_COLLATE=POSIX
# Used in error messages
readonly SCRIPT_NAME=${0##*/}
# Required in a few place. (Printing the trialing `x` ensures the `\r` won't be
# stripped, as it's not the last character before a `\n`.)
NEWLINE=$(printf \\nx)
readonly NEWLINE=${NEWLINE%x}
# Required by `PROMPT`.
CARRIAGE_RETURN=$(printf \\rx)
readonly CARRIAGE_RETURN=${CARRIAGE_RETURN%x}
################################################################################
# #
# Import Knight Files #
# #
################################################################################
# Get the directory containing this file. (This is a janky hack required in case
# the folder enclosing this file for some reason ends in a newline)
if ! KNIGHT_DIR=$(dirname -- "$0" && printf x); then
printf >&2 '%s: [FATAL] cannot get directory of this script\n' \
"$SCRIPT_NAME"
exit 3
fi
readonly KNIGHT_DIR=${KNIGHT_DIR%?x}
. "$KNIGHT_DIR/arrays.sh"
. "$KNIGHT_DIR/conversions.sh"
. "$KNIGHT_DIR/utilities.sh"
. "$KNIGHT_DIR/parsing.sh"
. "$KNIGHT_DIR/evaluating.sh"
################################################################################
# #
# Handle Command-line Arguments #
# #
################################################################################
# Print out the usage
usage () { cat; } <<EOS
usage: $SCRIPT_NAME [-h] (-e 'script' | -f filename)
EOS
# Print usage if `-h` given
if [ $# = 1 ] && [ "$1" = -h ]; then
usage
exit
fi
# If not exactly two arguments are given, error
if [ $# -ne 2 ]; then
usage >&2
exit 1
fi
# Handle `-e` or `-f`.
case $1 in
-e) next_expr <<EOS
$2
EOS
;;
-f) next_expr <"$2" ;; # Quoted b/c old bash versions don't like it.
*) usage >&2; exit 1;
esac
# Run the result of `next_expr` in the case statement
run "$Reply"