| 1 | #!/usr/bin/env python2
|
| 2 | """frontend/builtin_def.py.
|
| 3 |
|
| 4 | Metadata:
|
| 5 |
|
| 6 | - Is used for lookup in cmd_eval.py
|
| 7 | - Should be used for completion
|
| 8 | - complete names of builtins
|
| 9 | - complete flags they take
|
| 10 | - handle aliases : . and source, [ and test
|
| 11 | - Should be reflected in the contents of the 'help' builtin
|
| 12 |
|
| 13 | NOTE: bash has help -d -m -s. Default is -s, like a man page.
|
| 14 |
|
| 15 | Links on special builtins:
|
| 16 | http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_14
|
| 17 | """
|
| 18 | from __future__ import print_function
|
| 19 |
|
| 20 | from typing import Dict, List, Optional, Any
|
| 21 |
|
| 22 | # Special builtins can't be redefined by functions. On the other hand, 'cd'
|
| 23 | # CAN be redefined.
|
| 24 | #
|
| 25 | # http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_14
|
| 26 | # https://www.gnu.org/software/bash/manual/html_node/Special-Builtins.html
|
| 27 |
|
| 28 | # yapf: disable
|
| 29 | _NORMAL_BUILTINS = [
|
| 30 | 'read', 'echo', 'printf', 'mapfile', 'readarray',
|
| 31 |
|
| 32 | 'cd', 'pushd', 'popd', 'dirs', 'pwd',
|
| 33 |
|
| 34 | 'source', # note that . alias is special
|
| 35 |
|
| 36 | 'umask', 'ulimit', 'wait', 'jobs', 'fg', 'bg',
|
| 37 |
|
| 38 | 'shopt',
|
| 39 | 'complete', 'compgen', 'compopt', 'compadjust', 'compexport',
|
| 40 |
|
| 41 | 'getopts',
|
| 42 |
|
| 43 | # introspection / meta
|
| 44 | 'builtin', 'command', 'type', 'hash', 'help', 'history',
|
| 45 |
|
| 46 | 'alias', 'unalias',
|
| 47 | 'bind',
|
| 48 |
|
| 49 | #
|
| 50 | # YSH
|
| 51 | #
|
| 52 | 'append',
|
| 53 | 'write', 'json', 'json8', 'pp',
|
| 54 | 'hay', 'haynode',
|
| 55 | 'use',
|
| 56 | 'error', 'failed',
|
| 57 |
|
| 58 | # take a block
|
| 59 | # push-registers added below
|
| 60 | 'fork', 'forkwait',
|
| 61 | 'fopen',
|
| 62 | 'shvar',
|
| 63 | 'ctx',
|
| 64 |
|
| 65 | 'runproc',
|
| 66 | 'boolstatus',
|
| 67 | ]
|
| 68 | # yapf: enable
|
| 69 |
|
| 70 |
|
| 71 | class _Builtin(object):
|
| 72 |
|
| 73 | def __init__(self, index, name, enum_name=None, kind='normal'):
|
| 74 | # type: (int, str, Optional[str], str) -> None
|
| 75 | """
|
| 76 | kind: normal, special, assign
|
| 77 | """
|
| 78 | self.index = index
|
| 79 | self.name = name # e.g. : or [
|
| 80 | self.enum_name = enum_name or name # e.g. builtin_num::colon
|
| 81 | self.kind = kind
|
| 82 |
|
| 83 |
|
| 84 | class _BuiltinDef(object):
|
| 85 | """
|
| 86 | NOTE: This isn't used anywhere! We're registering nothing.
|
| 87 |
|
| 88 | We want to complete the flags to builtins. So this is a mapping from name
|
| 89 | to arg spec. There might not be any flags.
|
| 90 | """
|
| 91 |
|
| 92 | def __init__(self):
|
| 93 | # type: () -> None
|
| 94 | self.builtins = [] # type: List[_Builtin]
|
| 95 | self.index = 1 # start with 1
|
| 96 |
|
| 97 | def Add(self, *posargs, **kwargs):
|
| 98 | # type: (Any, Any) -> None
|
| 99 | # NOTE: *posargs works around flake8/pyflakes bug!
|
| 100 | self.builtins.append(_Builtin(self.index, *posargs, **kwargs))
|
| 101 | self.index += 1
|
| 102 |
|
| 103 |
|
| 104 | def _Init(b):
|
| 105 | # type: (_BuiltinDef) -> None
|
| 106 |
|
| 107 | #
|
| 108 | # Special builtins
|
| 109 | #
|
| 110 |
|
| 111 | b.Add(':', enum_name='colon', kind='special')
|
| 112 | b.Add('.', enum_name='dot', kind='special')
|
| 113 | # Python keyword
|
| 114 | b.Add('exec', enum_name='exec_', kind='special')
|
| 115 | for name in ['eval', 'set', 'shift', 'times', 'trap', 'unset']:
|
| 116 | b.Add(name, kind='special')
|
| 117 |
|
| 118 | #
|
| 119 | # Assignment builtins.
|
| 120 | # Note: control flow aren't builtins in OSH: break continue return
|
| 121 | #
|
| 122 |
|
| 123 | for name in ["readonly", "local", "declare", "typeset"]:
|
| 124 | b.Add(name, kind='assign')
|
| 125 | b.Add('export', enum_name='export_', kind='assign') # C++ keyword conflict
|
| 126 |
|
| 127 | b.Add('true', enum_name='true_') # C++ Keywords
|
| 128 | b.Add('false', enum_name='false_')
|
| 129 | b.Add('try', enum_name='try_')
|
| 130 | b.Add('assert', enum_name='assert_') # avoid Python keyword
|
| 131 |
|
| 132 | for name in _NORMAL_BUILTINS:
|
| 133 | b.Add(name)
|
| 134 |
|
| 135 | # Slight variants
|
| 136 | b.Add('test')
|
| 137 | b.Add('[', enum_name='bracket')
|
| 138 |
|
| 139 | b.Add('push-registers', enum_name='push_registers')
|
| 140 | b.Add('source-guard', enum_name='source_guard')
|
| 141 | b.Add('is-main', enum_name='is_main')
|
| 142 |
|
| 143 | # Implementation detail of $(<file)
|
| 144 | # TODO: change to 'internal cat' (issue 1013)
|
| 145 | b.Add('__cat', enum_name='cat')
|
| 146 |
|
| 147 |
|
| 148 | _BUILTIN_DEF = _BuiltinDef()
|
| 149 |
|
| 150 | _Init(_BUILTIN_DEF)
|
| 151 |
|
| 152 | # Exposed in consts.py for completion
|
| 153 | BUILTIN_NAMES = [b.name for b in _BUILTIN_DEF.builtins]
|
| 154 |
|
| 155 |
|
| 156 | def All():
|
| 157 | # type: () -> List[_Builtin]
|
| 158 | return _BUILTIN_DEF.builtins
|
| 159 |
|
| 160 |
|
| 161 | def BuiltinDict():
|
| 162 | # type: () -> Dict[str, _Builtin]
|
| 163 | """For the slow path in frontend/match.py."""
|
| 164 | return dict((b.name, b) for b in _BUILTIN_DEF.builtins)
|