View
@@ -23,4 +23,10 @@ version-text() {
_compare ./spec.sh version-text
}
count() {
# TODO: Need to implement 'return'
_compare ./count.sh all
#_compare ./count.sh parser
}
"$@"
View
@@ -10,6 +10,7 @@
"""
from core import base
from core import braces
from core import word
from core.id_kind import Id, Kind, REDIR_DEFAULT_FD
from core.util import log
@@ -186,73 +187,6 @@ def _NewlineOk(self):
return False
return True
# TODO: Hook up brace expansion! Does this work?
def _BraceExpandIter(): # one iteration
"""
Args:
words: list of words
Returns:
new_words: list of new words, or None
"""
expansions = [] # (pos, new_list)
for i, w in enumerate(words):
# Call CompoundWord.BraceExpand here. Returns a list of expansinos, or
# nullptr?
exp_words = w.BraceExpand()
if exp_words:
expansions.append((i, exp_words))
# If we got any expansions, create the new word list.
j = 0
if expansions:
new_words = []
for i, old_word in enumerate(words):
if j < len(expansions):
index, exp_words = expansions[j]
if index == i:
new_words.extend(exp_words)
else:
new_words.append(old_word)
return new_words
else:
return None
def _BraceExpand(self, words):
"""
Returns:
A list of new Word instances, or None if there was no brace expansion
detected.
"""
# Algorithm:
#
# Look for patterns like LBRACE COMMA RBRACE
# And then form cross product somehow.
# "A correctly-formed brace expansion must contain unquoted opening and
# closing braces, and at least one unquoted comma or a valid sequence
# expression. Any incorrectly formed brace expansion is left unchanged. "
# Could this be recursive? preamble,options,postscript
#
# Hm bash also has integer expressions! {1..3} => {1,2,3}
# {1..5..2} => {1,3,5}
# - mksh doesn't have it.
# look for subseqeuence like '{' ','+ '}'
# And then make a data structure for this.
return words
def _TildeDetectAll(self, words):
new_words = []
for w in words:
t = word.TildeDetect(w)
if t:
new_words.append(t)
else:
new_words.append(w)
return new_words
def _MaybeReadHereDocs(self, node):
here_docs = _GetHereDocsToFill(node)
#print('')
@@ -474,11 +408,14 @@ def _MakeSimpleCommand(self, prefix_bindings, suffix_words, redirects):
self.AddErrorContext('Unexpected array literal: %s', v, word=v)
return None
words2 = self._BraceExpand(suffix_words)
# NOTE: Must do tilde detection after brace expansion, e.g.
# {~bob,~jane}/src should work, even though ~ isn't the leading character
# of the initial word.
words3 = self._TildeDetectAll(words2)
# NOTE: # In bash, {~bob,~jane}/src works, even though ~ isn't the leading
# character of the initial word.
# However, this means we must do tilde detection AFTER brace EXPANSION, not
# just after brace DETECTION like we're doing here.
# The BracedWordTree instances have to be expanded into CompoundWord instances
# for the tilde detection to work.
words2 = braces.BraceDetectAll(suffix_words)
words3 = word.TildeDetectAll(words2)
node = ast.SimpleCommand()
node.words = words3
@@ -772,9 +709,12 @@ def _ParseForEachLoop(self):
in_spid = word.LeftMostSpanForWord(self.cur_word) + 1
iter_words, semi_spid = self.ParseForWords()
words2 = braces.BraceDetectAll(iter_words)
words3 = word.TildeDetectAll(words2)
if iter_words is None: # empty list of words is OK
return None
node.iter_words = iter_words
node.iter_words = words3
elif self.c_id == Id.Op_Semi:
node.do_arg_iter = True # implicit for loop
View
@@ -72,14 +72,23 @@ module osh
| TildeSubPart(string prefix)
| CommandSubPart(command command_list)
| ArithSubPart(arith_expr anode)
| AltPart(word* words) -- {a,b,c} is a single AltPart
| NumRangePart(int start, int end, int? step) -- {1..10} or {1..10..2}
| CharRangePart(string start, string end, int? step)
-- {a..f} or {a..f..2} or {a..f..-2}
-- {a,b,c}
| BracedAltPart(word* words)
-- {1..10} or {1..10..2}
| BracedIntRangePart(int start, int end, int? step)
-- {a..f} or {a..f..2} or {a..f..-2}
| BracedCharRangePart(string start, string end, int? step)
word =
TokenWord(token token)
-- A CompoundWord can contain any word_part except the Braced*Part.
-- We could model this with another variant type but it incurs runtime
-- overhead and seems like overkill. Note that DoubleQuotedPart can't
-- contain a SingleQuotedPart, etc. either.
| CompoundWord(word_part* parts)
-- A BracedWordTree is a word because it can appear in a command. It can
-- contains any type of word_part.
| BracedWordTree(word_part* parts)
lvalue =
LeftVar(string name)
View
@@ -12,6 +12,7 @@
from core import base
from core.id_kind import Id, Kind, IdName, LookupKind
from core import braces
from core import word
from core import tdop
@@ -832,21 +833,24 @@ def ReadForExpression(self):
return ast.ForExpr(init_node, cond_node, update_node)
def _ReadArrayLiteralPart(self):
array_part = ast.ArrayLiteralPart()
self._Next(LexMode.OUTER) # advance past (
self._Peek()
assert self.cur_token.id == Id.Op_LParen, self.cur_token
# MUST use a new word parser (with same lexer).
w_parser = WordParser(self.lexer, self.line_reader)
words = []
while True:
w = w_parser.ReadWord(LexMode.OUTER)
if word.CommandId(w) == Id.Right_ArrayLiteral:
break
array_part.words.append(w)
return array_part
words.append(w)
words2 = braces.BraceDetectAll(words)
words3 = word.TildeDetectAll(words2)
return ast.ArrayLiteralPart(words3)
def _ReadCompoundWord(self, eof_type=Id.Undefined_Tok, lex_mode=LexMode.OUTER,
empty_ok=True):
View
24 spec.sh
@@ -64,6 +64,14 @@ maybe-show() {
}
version-text() {
date
echo
local branch=$(git rev-parse --abbrev-ref HEAD)
local hash=$(git rev-parse $branch)
echo "oil version: $hash on branch $branch"
echo
python3 --version 2>&1
echo
@@ -168,13 +176,13 @@ word-split() {
# 'do' -- detected statically as syntax error? hm.
assign() {
sh-spec tests/assign.test.sh --osh-failures-allowed 3 \
sh-spec tests/assign.test.sh --osh-failures-allowed 1 \
${REF_SHELLS[@]} $OSH "$@"
}
# Need to fix $ tokens, and $''
quote() {
sh-spec tests/quote.test.sh --osh-failures-allowed 5 \
sh-spec tests/quote.test.sh --osh-failures-allowed 4 \
${REF_SHELLS[@]} $OSH "$@"
}
@@ -214,7 +222,7 @@ func() {
}
glob() {
sh-spec tests/glob.test.sh ${REF_SHELLS[@]} --osh-failures-allowed 6 \
sh-spec tests/glob.test.sh ${REF_SHELLS[@]} --osh-failures-allowed 1 \
$BUSYBOX_ASH $OSH "$@"
}
@@ -252,7 +260,7 @@ here-doc() {
redirect() {
# BUG: osh treats stdin as stdout! Fix this.
sh-spec tests/redirect.test.sh --osh-failures-allowed 12 \
sh-spec tests/redirect.test.sh --osh-failures-allowed 11 \
${REF_SHELLS[@]} $OSH "$@"
}
@@ -302,7 +310,7 @@ arith-context() {
# TODO: array= (a b c) vs array=(a b c). I think LookAheadForOp might still be
# messed up.
array() {
sh-spec tests/array.test.sh --osh-failures-allowed 30 \
sh-spec tests/array.test.sh --osh-failures-allowed 27 \
$BASH $MKSH $OSH "$@"
}
@@ -335,9 +343,9 @@ dparen() {
}
brace-expansion() {
# NOTE: being a korn shell, mksh has brace expansion. But dash doesn't!
# numeric ranges come from ZSH? mksh doesn't have them.
sh-spec tests/brace-expansion.test.sh $BASH $MKSH $ZSH "$@"
# TODO for osh: implement num ranges, mark char ranges unimplemented?
sh-spec tests/brace-expansion.test.sh --osh-failures-allowed 13 \
$BASH $MKSH $ZSH $OSH "$@"
}
regex() {
View
@@ -289,3 +289,14 @@ array2=("$@")
array3="$@" # Without splicing with (), this one is flattened
argv.py "${array1[@]}" "${array2[@]}" "${array3[@]}"
# stdout: ['x y', 'z', 'a b', 'c', 'a b c']
### Tilde expansion within array
HOME=/home/bob
a=(~/src ~/git)
echo "${a[@]}"
# stdout: /home/bob/src /home/bob/git
### Brace Expansion within Array
a=(-{a,b} {c,d}-)
echo "${a[@]}"
# stdout: -a -b c- d-
View
@@ -7,14 +7,27 @@ echo {foo}
### incomplete trailing expansion
echo {a,b}_{
# stdout: a_{ b_{
# OK osh stdout: {a,b}_{
### partial leading expansion
echo }_{a,b}
# stdout: }_a }_b
# OK osh stdout: }_{a,b}
### partial leading expansion 2
echo {x}_{a,b}
# stdout: {x}_a {x}_b
# OK osh stdout: {x}_{a,b}
### } in expansion
# hm they treat this the SAME. Leftmost { is matched by first }, and then
# there is another } as the postfix.
echo {a,b}}
# stdout: a} b}
# status: 0
# OK osh stdout: {a,b}}
# OK zsh stdout-json: ""
# OK zsh status: 1
### single expansion
echo {foo,bar}
@@ -87,15 +100,6 @@ echo {{a,b}
echo \{{a,b}
# stdout: {a {b
### } in expansion
# hm they treat this the SAME. Leftmost { is matched by first }, and then
# there is another } as the postfix.
echo {a,b}}
# stdout: a} b}
# status 0
# OK zsh stdout-json: ""
# OK zsh status: 1
### Empty expansion
echo a{X,,Y}b
# stdout: aXb ab aYb
View
@@ -27,6 +27,20 @@ done
# status: 2
# OK bash/mksh status: 1
### Tilde expansion within for loop
HOME=/home/bob
for name in ~/src ~/git; do
echo $name
done
# stdout-json: "/home/bob/src\n/home/bob/git\n"
### Brace Expansion within Array
for i in -{a,b} {c,d}-; do
echo $i
done
# stdout-json: "-a\n-b\nc-\nd-\n"
# N-I dash stdout-json: "-{a,b}\n{c,d}-\n"
### using loop var outside loop
func() {
for i in a b c; do