From c503d2dba44ecbc34d82aa714c3e62f4dfcd9481 Mon Sep 17 00:00:00 2001 From: Andy Chu Date: Tue, 14 Jul 2020 16:57:06 -0700 Subject: [PATCH] [translation] ctx_Alias Hm this doesn't fix the infinite loop in case #3 of spec/alias? Still need to work on that. mylib: add comments and test cases --- frontend/parse_lib.py | 59 ++++++++++++++++++++++++------------------- mycpp/mylib.h | 8 +++--- mycpp/mylib_test.cc | 7 ++++- osh/cmd_parse.py | 24 +++++++++--------- 4 files changed, 55 insertions(+), 43 deletions(-) diff --git a/frontend/parse_lib.py b/frontend/parse_lib.py index bcbe837fa4..e190cb1eec 100644 --- a/frontend/parse_lib.py +++ b/frontend/parse_lib.py @@ -62,7 +62,7 @@ def __init__(self): self.tokens = [] # type: List[Token] self.alias_words = [] # type: List[compound_word] # words INSIDE an alias expansion - self.expanding_alias = False + self._expanding_alias = False def Clear(self): # type: () -> None @@ -120,6 +120,36 @@ class _NullTrail(_BaseTrail): pass +class ctx_Alias(object): + """Used by CommandParser so we know to be ready for FIRST alias word. + + For example, for + + alias ll='ls -l' + + Then we want to capture 'ls' as the first word. + + We do NOT want SetLatestWords or AppendToken to be active, because we don't + need other tokens from 'ls -l'. + + It would also probably cause bugs in history expansion, e.g. echo !1 should + be the first word the user typed, not the first word after alias expansion. + """ + + def __init__(self, trail): + # type: (_BaseTrail) -> None + trail._expanding_alias = True + self.trail = trail + + def __enter__(self): + # type: () -> None + pass + + def __exit__(self, type, value, traceback): + # type: (Any, Any, Any) -> None + self.trail._expanding_alias = False + + class Trail(_BaseTrail): """Info left by the parser to help us complete shell syntax and commands. @@ -135,7 +165,7 @@ def Clear(self): def SetLatestWords(self, words, redirects): # type: (List[compound_word], List[redir]) -> None - if self.expanding_alias: + if self._expanding_alias: self.alias_words = words # Save these separately return self.words = words @@ -143,33 +173,10 @@ def SetLatestWords(self, words, redirects): def AppendToken(self, token): # type: (Token) -> None - if self.expanding_alias: # We don't want tokens inside aliases + if self._expanding_alias: # We don't want tokens inside aliases return self.tokens.append(token) - def BeginAliasExpansion(self): - # type: () -> None - """Called by CommandParser so we know to be ready for FIRST alias word. - - For example, for - - alias ll='ls -l' - - Then we want to capture 'ls' as the first word. - - We do NOT want SetLatestWords or AppendToken to be active, because we don't - need other tokens from 'ls -l'. - - It would also probably cause bugs in history expansion, e.g. echo !1 should - be the first word the user typed, not the first word after alias expansion. - """ - self.expanding_alias = True - - def EndAliasExpansion(self): - # type: () -> None - """Go back to the normal trail collection mode.""" - self.expanding_alias = False - if TYPE_CHECKING: AliasesInFlight = List[Tuple[str, int]] diff --git a/mycpp/mylib.h b/mycpp/mylib.h index 321d70052a..c7da540b93 100644 --- a/mycpp/mylib.h +++ b/mycpp/mylib.h @@ -894,10 +894,10 @@ inline int ord(Str* s) { } // https://stackoverflow.com/questions/3919995/determining-sprintf-buffer-size-whats-the-standard/11092994#11092994 -// Note: Python 2.7's intobject.c has an erroneous +6 - -// This is 13, but -// len('-2147483648') is 11, which means we only need 12? +// Notes: +// - Python 2.7's intobject.c has an erroneous +6 +// - This is 13, but len('-2147483648') is 11, which means we only need 12? +// - This formula is valid for octal(), because 2^(3 bits) = 8 const int kIntBufSize = CHAR_BIT * sizeof(int) / 3 + 3; inline Str* str(int i) { diff --git a/mycpp/mylib_test.cc b/mycpp/mylib_test.cc index c1361e3a82..3ba461c36c 100644 --- a/mycpp/mylib_test.cc +++ b/mycpp/mylib_test.cc @@ -143,17 +143,22 @@ TEST test_str_funcs() { int_str = str(-(1 << 31) + 1); log("i = %s", int_str->data_); - int_str = str(-(1 << 31)); + + int int_min = -(1 << 31); + int_str = str(int_min); log("i = %s", int_str->data_); int_str = mylib::hex_lower(15); ASSERT(str_equals0("f", int_str)); + print(mylib::hex_lower(int_min)); // ASAN implicitly checks this int_str = mylib::hex_upper(15); ASSERT(str_equals0("F", int_str)); + print(mylib::hex_upper(int_min)); // ASAN int_str = mylib::octal(15); ASSERT(str_equals0("17", int_str)); + print(mylib::octal(int_min)); // ASAN Str* s1 = new Str("abc\0bcd", 7); ASSERT_EQ(7, len(s1)); diff --git a/osh/cmd_parse.py b/osh/cmd_parse.py index 7f281fa1f9..4e76a6eec9 100644 --- a/osh/cmd_parse.py +++ b/osh/cmd_parse.py @@ -728,22 +728,22 @@ def _MaybeExpandAliases(self, words): cp = self.parse_ctx.MakeOshParser(line_reader) cp.Init_AliasesInFlight(aliases_in_flight) + # break circular dep + from frontend import parse_lib + # The interaction between COMPLETION and ALIASES requires special care. # See docstring of BeginAliasExpansion() in parse_lib.py. src = source.Alias(first_word_str, argv0_spid) with alloc.ctx_Location(self.arena, src): - trail = self.parse_ctx.trail - trail.BeginAliasExpansion() - try: - # _ParseCommandTerm() handles multiline commands, compound commands, etc. - # as opposed to ParseLogicalLine() - node = cp._ParseCommandTerm() - except error.Parse as e: - # Failure to parse alias expansion is a fatal error - # We don't need more handling here/ - raise - finally: - trail.EndAliasExpansion() + with parse_lib.ctx_Alias(self.parse_ctx.trail): + try: + # _ParseCommandTerm() handles multiline commands, compound commands, etc. + # as opposed to ParseLogicalLine() + node = cp._ParseCommandTerm() + except error.Parse as e: + # Failure to parse alias expansion is a fatal error + # We don't need more handling here/ + raise if 0: log('AFTER expansion:')