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 | 'redir', 'fopen', # fopen is for backward compat
|
62 | 'shvar',
|
63 | 'ctx',
|
64 |
|
65 | 'invoke',
|
66 | 'runproc',
|
67 | 'boolstatus',
|
68 | ]
|
69 | # yapf: enable
|
70 |
|
71 |
|
72 | class _Builtin(object):
|
73 |
|
74 | def __init__(self, index, name, enum_name=None, kind='normal'):
|
75 | # type: (int, str, Optional[str], str) -> None
|
76 | """
|
77 | kind: normal, special, assign
|
78 | """
|
79 | self.index = index
|
80 | self.name = name # e.g. : or [
|
81 | self.enum_name = enum_name or name # e.g. builtin_num::colon
|
82 | self.kind = kind
|
83 |
|
84 |
|
85 | class _BuiltinDef(object):
|
86 | """
|
87 | NOTE: This isn't used anywhere! We're registering nothing.
|
88 |
|
89 | We want to complete the flags to builtins. So this is a mapping from name
|
90 | to arg spec. There might not be any flags.
|
91 | """
|
92 |
|
93 | def __init__(self):
|
94 | # type: () -> None
|
95 | self.builtins = [] # type: List[_Builtin]
|
96 | self.index = 1 # start with 1
|
97 |
|
98 | def Add(self, *posargs, **kwargs):
|
99 | # type: (Any, Any) -> None
|
100 | # NOTE: *posargs works around flake8/pyflakes bug!
|
101 | self.builtins.append(_Builtin(self.index, *posargs, **kwargs))
|
102 | self.index += 1
|
103 |
|
104 |
|
105 | def _Init(b):
|
106 | # type: (_BuiltinDef) -> None
|
107 |
|
108 | #
|
109 | # Special builtins
|
110 | #
|
111 |
|
112 | b.Add(':', enum_name='colon', kind='special')
|
113 | b.Add('.', enum_name='dot', kind='special')
|
114 | # Python keyword
|
115 | b.Add('exec', enum_name='exec_', kind='special')
|
116 | for name in ['eval', 'set', 'shift', 'times', 'trap', 'unset']:
|
117 | b.Add(name, kind='special')
|
118 |
|
119 | #
|
120 | # Assignment builtins.
|
121 | # Note: control flow aren't builtins in OSH: break continue return
|
122 | #
|
123 |
|
124 | for name in ["readonly", "local", "declare", "typeset"]:
|
125 | b.Add(name, kind='assign')
|
126 | b.Add('export', enum_name='export_', kind='assign') # C++ keyword conflict
|
127 |
|
128 | b.Add('extern', enum_name='extern_')
|
129 | b.Add('true', enum_name='true_') # C++ Keywords
|
130 | b.Add('false', enum_name='false_')
|
131 | b.Add('try', enum_name='try_')
|
132 | b.Add('assert', enum_name='assert_') # avoid Python keyword
|
133 |
|
134 | for name in _NORMAL_BUILTINS:
|
135 | b.Add(name)
|
136 |
|
137 | # Slight variants
|
138 | b.Add('test')
|
139 | b.Add('[', enum_name='bracket')
|
140 |
|
141 | b.Add('push-registers', enum_name='push_registers')
|
142 | b.Add('source-guard', enum_name='source_guard')
|
143 | b.Add('is-main', enum_name='is_main')
|
144 |
|
145 | # Implementation detail of $(<file)
|
146 | # TODO: change to 'internal cat' (issue 1013)
|
147 | b.Add('__cat', enum_name='cat')
|
148 |
|
149 |
|
150 | _BUILTIN_DEF = _BuiltinDef()
|
151 |
|
152 | _Init(_BUILTIN_DEF)
|
153 |
|
154 | # Exposed in consts.py for completion
|
155 | BUILTIN_NAMES = [b.name for b in _BUILTIN_DEF.builtins]
|
156 |
|
157 |
|
158 | def All():
|
159 | # type: () -> List[_Builtin]
|
160 | return _BUILTIN_DEF.builtins
|
161 |
|
162 |
|
163 | def BuiltinDict():
|
164 | # type: () -> Dict[str, _Builtin]
|
165 | """For the slow path in frontend/match.py."""
|
166 | return dict((b.name, b) for b in _BUILTIN_DEF.builtins)
|