Skip to content

Commit

Permalink
Implement the 'type' builtin (just the '-t' case), with tests.
Browse files Browse the repository at this point in the history
Used by setup.sh in Nix.

Addresses issue #26.
  • Loading branch information
Andy Chu committed Sep 6, 2017
1 parent bbe5808 commit 5aaffbc
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 2 deletions.
60 changes: 59 additions & 1 deletion core/builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
TRUE FALSE
COLON
TEST BRACKET
HELP
TYPE HELP
""".split())


Expand Down Expand Up @@ -197,6 +197,9 @@ def Resolve(argv0):
elif argv0 == "[":
return EBuiltin.BRACKET

elif argv0 == "type":
return EBuiltin.TYPE

elif argv0 == "help":
return EBuiltin.HELP

Expand Down Expand Up @@ -646,6 +649,61 @@ def Unset(argv, mem, funcs):
return 0


TYPE_SPEC = _Register('type')
TYPE_SPEC.ShortFlag('-t')

def Type(argv, funcs, path_val):
arg, i = TYPE_SPEC.Parse(argv)

if path_val.tag == value_e.Str:
path_list = path_val.s.split(':')
else:
path_list = [] # treat as empty path

status = 0
if not arg.t:
util.warn("*** 'type' builtin called without -t ***")
status = 1
# Keep going anyway

for name in argv[i:]:
if name in funcs:
print('function')
continue

if Resolve(name) != EBuiltin.NONE:
print('builtin')
continue
if ResolveSpecial(name) != EBuiltin.NONE:
print('builtin')
continue
if lex.IsOtherBuiltin(name):
print('builtin')
continue

if lex.IsKeyword(name):
print('keyword')
continue

# Now look for files.
found = False
for path_dir in path_list:
full_path = os.path.join(path_dir, name)
if os.path.exists(full_path):
print('file')
found = True
break
if found:
continue

# Name not found. Nothing printed, but status is 1.
#log('%r not found', name)
status = 1

return status



def Trap(argv, traps):
# TODO: register trap

Expand Down
4 changes: 4 additions & 0 deletions core/cmd_exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,10 @@ def _RunBuiltin(self, builtin_id, argv):
elif builtin_id == EBuiltin.BRACKET:
status = test_builtin.Test(argv, True) # need_right_bracket

elif builtin_id == EBuiltin.TYPE:
path = self.mem.GetVar('PATH')
status = builtin.Type(argv, self.funcs, path)

elif builtin_id == EBuiltin.HELP:
loader = util.GetResourceLoader()
status = builtin.Help(argv, loader)
Expand Down
20 changes: 19 additions & 1 deletion osh/lex.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,11 @@
C('elif', Id.KW_Elif),
C('function', Id.KW_Function),
C('time', Id.KW_Time),
]

# These are treated like builtins in bash, but keywords in OSH. However, we
# main compatibility with bash for the 'type' builtin.
_MORE_KEYWORDS = [
C('declare', Id.Assign_Declare),
C('local', Id.Assign_Local),
C('readonly', Id.Assign_Readonly),
Expand All @@ -191,13 +195,27 @@
C('return', Id.ControlFlow_Return),
]


_TYPE_KEYWORDS = set(name for _, name, _ in _KEYWORDS)
_TYPE_KEYWORDS.add('{') # not in our lexer list
_TYPE_BUILTINS = set(name for _, name, _ in _MORE_KEYWORDS)


def IsOtherBuiltin(name):
return name in _TYPE_BUILTINS


def IsKeyword(name):
return name in _TYPE_KEYWORDS


# These two can must be recognized in the OUTER state, but can't nested within
# [[.
# Keywords have to be checked before _UNQUOTED so we get <KW_If "if"> instead
# of <Lit_Chars "if">.
LEXER_DEF[LexMode.OUTER] = [
C('((', Id.Op_DLeftParen), # not allowed within [[
] + _KEYWORDS + _UNQUOTED
] + _KEYWORDS + _MORE_KEYWORDS + _UNQUOTED

# DBRACKET: can be like OUTER, except:
# - Don't really need redirects either... Redir_Less could be Op_Less
Expand Down
27 changes: 27 additions & 0 deletions spec/builtin-type.test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/bash
#
# Bash implements type -t.
#
# NOTE: Aliases don't work in batch mode! Interactive only.

### type -t builtin -> function
f() { echo hi; }
type -t f
# stdout-json: "function\n"

### type -t builtin -> builtin
type -t echo read : [ declare local break continue
# stdout-json: "builtin\nbuiltin\nbuiltin\nbuiltin\nbuiltin\nbuiltin\nbuiltin\nbuiltin\n"

### type -t builtin -> keyword
type -t for time ! fi do {
# stdout-json: "keyword\nkeyword\nkeyword\nkeyword\nkeyword\nkeyword\n"

### type -t builtin -> file
type -t find xargs
# stdout-json: "file\nfile\n"

### type -t builtin -> not found
type -t echo ZZZ find =
echo status=$?
# stdout-json: "builtin\nfile\nstatus=1\n"
6 changes: 6 additions & 0 deletions test/spec.sh
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,12 @@ builtin-vars() {
${REF_SHELLS[@]} $OSH "$@"
}

# Bash impleement type -t, but no other shell does. For Nix.
builtin-type() {
sh-spec spec/builtin-type.test.sh \
$BASH $OSH "$@"
}

builtins-special() {
sh-spec spec/builtins-special.test.sh --osh-failures-allowed 3 \
${REF_SHELLS[@]} $OSH "$@"
Expand Down

0 comments on commit 5aaffbc

Please sign in to comment.