2014-05-09 00:46:18 -04:00
|
|
|
#!/bin/bash
|
|
|
|
# Parser stack
|
|
|
|
#
|
2014-06-17 01:35:24 -04:00
|
|
|
# Copyright (C) 2014 Mike Gerwitz
|
2014-05-09 00:46:18 -04:00
|
|
|
#
|
|
|
|
# This file is part of shspec.
|
|
|
|
#
|
|
|
|
# shspec is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
##
|
|
|
|
|
|
|
|
[ -z $__INC_SPECSTACK ] || return
|
|
|
|
__INC_SPECSTACK=1
|
|
|
|
|
|
|
|
declare -a __sstack=()
|
|
|
|
declare -i __sstackp=0
|
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
# Push a frame onto the stack
|
2017-05-10 01:10:14 -04:00
|
|
|
shspec:stack:_push()
|
2014-05-09 00:46:18 -04:00
|
|
|
{
|
|
|
|
local -r type="$1"
|
|
|
|
local -r srcline="$2"
|
|
|
|
local -r srcfile="$3"
|
|
|
|
shift 3
|
|
|
|
|
|
|
|
__sstack[$__sstackp]="$type|$srcline|$srcfile|$*"
|
|
|
|
((__sstackp++))
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
# Pop a frame from the stack
|
|
|
|
#
|
|
|
|
# It is possible to recover the most recently popped frame.
|
2017-05-10 01:10:14 -04:00
|
|
|
shspec:stack:_pop()
|
2014-05-09 00:46:18 -04:00
|
|
|
{
|
|
|
|
[ "$__sstackp" -gt 0 ] || return 1
|
|
|
|
|
|
|
|
# notice that we unset before we decrease the pointer; this allows for a
|
|
|
|
# single level of un-popping
|
|
|
|
unset __sstack[$__sstackp]
|
|
|
|
((__sstackp--))
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
# Recover the most recently popped frame
|
|
|
|
#
|
|
|
|
# Note that this should never be called more than once in an attempt to
|
|
|
|
# recover additional frames; it will not work, and you will make bad things
|
|
|
|
# happen, and people will hate you.
|
2017-05-10 01:10:14 -04:00
|
|
|
shspec:stack:_unpop()
|
2014-05-09 00:46:18 -04:00
|
|
|
{
|
|
|
|
((__sstackp++))
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
# Return with a non-zero status only if the stack is non-empty
|
2017-05-10 01:10:14 -04:00
|
|
|
shspec:stack:_empty()
|
2014-05-09 00:46:18 -04:00
|
|
|
{
|
|
|
|
test "$__sstackp" -eq 0
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
# Output the current size of the stack
|
2017-05-10 01:10:14 -04:00
|
|
|
shspec:stack:_size()
|
2014-05-09 00:46:18 -04:00
|
|
|
{
|
|
|
|
echo "$__sstackp"
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
# Output the current stack frame
|
2017-05-10 01:10:14 -04:00
|
|
|
shspec:stack:_head()
|
2014-05-09 00:46:18 -04:00
|
|
|
{
|
|
|
|
local -ri headi=$((__sstackp-1))
|
|
|
|
echo "${__sstack[$headi]}"
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
# Output the type of the current stack frame
|
2017-05-10 01:10:14 -04:00
|
|
|
shspec:stack:_head-type()
|
2014-05-09 00:46:18 -04:00
|
|
|
{
|
2017-05-10 01:10:14 -04:00
|
|
|
_shspec:stack:_headn 0
|
2014-05-09 00:46:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
# Output the Nth datum of the current stack frame
|
2017-05-10 01:10:14 -04:00
|
|
|
_shspec:stack:_headn()
|
2014-05-09 00:46:18 -04:00
|
|
|
{
|
|
|
|
local -ri i="$1"
|
|
|
|
local parts
|
|
|
|
|
2017-05-10 01:10:14 -04:00
|
|
|
shspec:stack:_read -a parts <<< "$(shspec:stack:_head)"
|
2014-05-09 00:46:18 -04:00
|
|
|
echo "${parts[$i]}"
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
# Deconstruct stack frame from stdin in a `read`-like manner
|
2017-05-10 01:10:14 -04:00
|
|
|
shspec:stack:_read()
|
2014-05-09 00:46:18 -04:00
|
|
|
{
|
|
|
|
IFS=\| read "$@"
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
# Deconstruct current stack frame in a `read`-like manner
|
|
|
|
#
|
|
|
|
# Return immediately with a non-zero status if there are no frames on the
|
|
|
|
# stack.
|
2017-05-10 01:10:14 -04:00
|
|
|
shspec:stack:_read-pop()
|
2014-05-09 00:46:18 -04:00
|
|
|
{
|
2017-05-10 01:10:14 -04:00
|
|
|
local -r head="$(shspec:stack:_pop)" || return 1
|
|
|
|
shspec:stack:_read "$@" <<< "$head"
|
2014-05-09 00:46:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
# Assert that the immediately preceding frame is of the given type
|
|
|
|
#
|
|
|
|
# Conceptually, this allows determining if the parent node in a tree-like
|
|
|
|
# structure is of a certain type.
|
2017-05-10 01:10:14 -04:00
|
|
|
shspec:stack:_assert-within()
|
2014-05-09 00:46:18 -04:00
|
|
|
{
|
|
|
|
local -r in="$1"
|
|
|
|
local -r chk="$2"
|
|
|
|
local -ri line="$3"
|
|
|
|
local -r file="$4"
|
|
|
|
local -r phrase="${5:-be contained within}"
|
|
|
|
|
2017-05-10 01:10:14 -04:00
|
|
|
local -r head="$(shspec:stack:_head-type)"
|
2014-05-09 00:46:18 -04:00
|
|
|
|
|
|
|
[ "$head" == "$in" ] \
|
2017-05-10 01:10:14 -04:00
|
|
|
|| shspec:bail \
|
2014-06-13 17:10:02 -04:00
|
|
|
"\`$chk' must $phrase \`$head'; found \`$head' at $file:$line"
|
2014-05-09 00:46:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
##
|
2017-05-10 01:10:14 -04:00
|
|
|
# Alias for shspec:stack:_assert-within with altered error message
|
2014-05-09 00:46:18 -04:00
|
|
|
#
|
|
|
|
# This is intended to convey a different perspective: that a given node is a
|
|
|
|
# sibling, not a child, in a tree-like structure.
|
2017-05-10 01:10:14 -04:00
|
|
|
shspec:stack:_assert-follow()
|
2014-05-09 00:46:18 -04:00
|
|
|
{
|
2017-05-10 01:10:14 -04:00
|
|
|
shspec:stack:_assert-within "$@" follow
|
2014-05-09 00:46:18 -04:00
|
|
|
}
|
|
|
|
|