| 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 | 
 | 
| 131 |     for name in _NORMAL_BUILTINS:
 | 
| 132 |         b.Add(name)
 | 
| 133 | 
 | 
| 134 |     # Slight variants
 | 
| 135 |     b.Add('test')
 | 
| 136 |     b.Add('[', enum_name='bracket')
 | 
| 137 | 
 | 
| 138 |     b.Add('push-registers', enum_name='push_registers')
 | 
| 139 |     b.Add('source-guard', enum_name='source_guard')
 | 
| 140 |     b.Add('is-main', enum_name='is_main')
 | 
| 141 | 
 | 
| 142 |     # Implementation detail of $(<file)
 | 
| 143 |     # TODO: change to 'internal cat' (issue 1013)
 | 
| 144 |     b.Add('__cat', enum_name='cat')
 | 
| 145 | 
 | 
| 146 | 
 | 
| 147 | _BUILTIN_DEF = _BuiltinDef()
 | 
| 148 | 
 | 
| 149 | _Init(_BUILTIN_DEF)
 | 
| 150 | 
 | 
| 151 | # Exposed in consts.py for completion
 | 
| 152 | BUILTIN_NAMES = [b.name for b in _BUILTIN_DEF.builtins]
 | 
| 153 | 
 | 
| 154 | 
 | 
| 155 | def All():
 | 
| 156 |     # type: () -> List[_Builtin]
 | 
| 157 |     return _BUILTIN_DEF.builtins
 | 
| 158 | 
 | 
| 159 | 
 | 
| 160 | def BuiltinDict():
 | 
| 161 |     # type: () -> Dict[str, _Builtin]
 | 
| 162 |     """For the slow path in frontend/match.py."""
 | 
| 163 |     return dict((b.name, b) for b in _BUILTIN_DEF.builtins)
 |