| 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 | from __future__ import print_function
|
| 16 |
|
| 17 | from typing import Dict, List, Optional, Any
|
| 18 |
|
| 19 | # Special builtins can't be redefined by functions. On the other hand, 'cd'
|
| 20 | # CAN be redefined.
|
| 21 | #
|
| 22 | # http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_14
|
| 23 | # https://www.gnu.org/software/bash/manual/html_node/Special-Builtins.html
|
| 24 |
|
| 25 | # yapf: disable
|
| 26 | _NORMAL_BUILTINS = [
|
| 27 | 'read', 'echo', 'printf', 'mapfile', 'readarray',
|
| 28 |
|
| 29 | 'cd', 'pushd', 'popd', 'dirs', 'pwd',
|
| 30 |
|
| 31 | 'source', # note that . alias is special
|
| 32 |
|
| 33 | 'umask', 'ulimit', 'wait', 'jobs', 'fg', 'bg',
|
| 34 |
|
| 35 | 'shopt',
|
| 36 | 'complete', 'compgen', 'compopt', 'compadjust', 'compexport',
|
| 37 |
|
| 38 | 'getopts',
|
| 39 |
|
| 40 | # introspection / meta
|
| 41 | 'builtin', 'command', 'type', 'hash', 'help', 'history',
|
| 42 |
|
| 43 | 'alias', 'unalias',
|
| 44 | 'bind',
|
| 45 |
|
| 46 | #
|
| 47 | # YSH
|
| 48 | #
|
| 49 | 'append',
|
| 50 | 'write', 'json', 'json8', 'pp',
|
| 51 | 'hay', 'haynode',
|
| 52 | 'use',
|
| 53 | 'error', 'failed',
|
| 54 |
|
| 55 | # take a block
|
| 56 | # push-registers added below
|
| 57 | 'fork', 'forkwait',
|
| 58 | 'redir', 'fopen', # fopen is for backward compat
|
| 59 | 'shvar',
|
| 60 | 'ctx',
|
| 61 |
|
| 62 | 'invoke',
|
| 63 | 'runproc',
|
| 64 | 'boolstatus',
|
| 65 | ]
|
| 66 | # yapf: enable
|
| 67 |
|
| 68 |
|
| 69 | class _Builtin(object):
|
| 70 |
|
| 71 | def __init__(self, index, name, enum_name=None, kind='normal'):
|
| 72 | # type: (int, str, Optional[str], str) -> None
|
| 73 | """
|
| 74 | kind: normal, special, assign, intern
|
| 75 | """
|
| 76 | self.index = index
|
| 77 | self.name = name # e.g. : or [
|
| 78 | self.enum_name = enum_name or name # e.g. builtin_num::colon
|
| 79 | self.kind = kind
|
| 80 |
|
| 81 |
|
| 82 | class _BuiltinDef(object):
|
| 83 | """
|
| 84 | NOTE: This isn't used anywhere! We're registering nothing.
|
| 85 |
|
| 86 | We want to complete the flags to builtins. So this is a mapping from name
|
| 87 | to arg spec. There might not be any flags.
|
| 88 | """
|
| 89 |
|
| 90 | def __init__(self):
|
| 91 | # type: () -> None
|
| 92 | self.builtins = [] # type: List[_Builtin]
|
| 93 | self.index = 1 # start with 1
|
| 94 |
|
| 95 | def Add(self, *posargs, **kwargs):
|
| 96 | # type: (Any, Any) -> None
|
| 97 | # NOTE: *posargs works around flake8/pyflakes bug!
|
| 98 | self.builtins.append(_Builtin(self.index, *posargs, **kwargs))
|
| 99 | self.index += 1
|
| 100 |
|
| 101 |
|
| 102 | def _Init(b):
|
| 103 | # type: (_BuiltinDef) -> None
|
| 104 |
|
| 105 | #
|
| 106 | # Special builtins
|
| 107 | #
|
| 108 | # List:
|
| 109 | # http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_14
|
| 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('extern', enum_name='extern_')
|
| 128 | b.Add('true', enum_name='true_') # C++ Keywords
|
| 129 | b.Add('false', enum_name='false_')
|
| 130 | b.Add('try', enum_name='try_')
|
| 131 | b.Add('assert', enum_name='assert_') # avoid Python keyword
|
| 132 |
|
| 133 | # Control flow
|
| 134 | b.Add('break', enum_name='break_')
|
| 135 | b.Add('continue', enum_name='continue_')
|
| 136 | b.Add('return', enum_name='return_')
|
| 137 | b.Add('exit')
|
| 138 |
|
| 139 | for name in _NORMAL_BUILTINS:
|
| 140 | b.Add(name)
|
| 141 |
|
| 142 | # Slight variants
|
| 143 | b.Add('test')
|
| 144 | b.Add('[', enum_name='bracket')
|
| 145 |
|
| 146 | b.Add('push-registers', enum_name='push_registers')
|
| 147 | b.Add('source-guard', enum_name='source_guard')
|
| 148 | b.Add('is-main', enum_name='is_main')
|
| 149 |
|
| 150 | # Private builtins
|
| 151 | for name in ['cat', 'rm', 'sleep']:
|
| 152 | b.Add(name, kind='private')
|
| 153 |
|
| 154 |
|
| 155 | _BUILTIN_DEF = _BuiltinDef()
|
| 156 |
|
| 157 | _Init(_BUILTIN_DEF)
|
| 158 |
|
| 159 | # Exposed in consts.py for completion
|
| 160 | BUILTIN_NAMES = [b.name for b in _BUILTIN_DEF.builtins if b.kind != 'private']
|
| 161 |
|
| 162 |
|
| 163 | def All():
|
| 164 | # type: () -> List[_Builtin]
|
| 165 | return _BUILTIN_DEF.builtins
|
| 166 |
|
| 167 |
|
| 168 | def BuiltinDict():
|
| 169 | # type: () -> Dict[str, _Builtin]
|
| 170 | """For the slow path in frontend/match.py."""
|
| 171 | return dict((b.name, b) for b in _BUILTIN_DEF.builtins)
|