Skip to content

Commit

Permalink
[builtins/ysh] Add first pass of formatDebugFrame()
Browse files Browse the repository at this point in the history
Add debug_frame.MainEntry(), for symmetry.

We might only use 'Call' and 'Source'.  Because each of those will have
a token, and then the first one may be in one of:

    main file
    [ -c flag ]
    [ stdin ]
  • Loading branch information
Andy C committed Feb 9, 2025
1 parent 15ba044 commit b3ef6f4
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 25 deletions.
37 changes: 34 additions & 3 deletions builtin/func_reflect.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"""
from __future__ import print_function

from _devbuild.gen.runtime_asdl import (scope_e)
from _devbuild.gen.syntax_asdl import source
from _devbuild.gen.runtime_asdl import scope_e
from _devbuild.gen.syntax_asdl import (source, debug_frame, debug_frame_e)
from _devbuild.gen.value_asdl import (value, value_e, value_t, cmd_frag)

from core import alloc
Expand All @@ -20,7 +20,7 @@
from mycpp import mops
from mycpp.mylib import log, tagswitch

from typing import List, TYPE_CHECKING
from typing import List, cast, TYPE_CHECKING
if TYPE_CHECKING:
from frontend import parse_lib
from display import ui
Expand Down Expand Up @@ -120,6 +120,37 @@ def Call(self, rd):
return value.List(debug_frames)


class FormatDebugFrame(vm._Callable):

def __init__(self):
# type: () -> None
vm._Callable.__init__(self)

def Call(self, rd):
# type: (typed_args.Reader) -> value_t
frame = rd.PosDebugFrame()
rd.Done()

UP_frame = frame
result = ''
with tagswitch(frame) as case:
if case(debug_frame_e.MainEntry):
frame = cast(debug_frame.MainEntry, UP_frame)
result = 'main entry %s' % frame.description
elif case(debug_frame_e.MainFile):
frame = cast(debug_frame.MainFile, UP_frame)
result = 'main file %s' % frame.main_filename
elif case(debug_frame_e.Call):
frame = cast(debug_frame.Call, UP_frame)
result = 'call'
elif case(debug_frame_e.Source):
frame = cast(debug_frame.Source, UP_frame)
result = 'source'
else:
raise AssertionError()
return value.Str(result)


class Shvar_get(vm._Callable):
"""Look up with dynamic scope."""

Expand Down
23 changes: 13 additions & 10 deletions core/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,19 +287,20 @@ def Main(
status = 1
return status

debug_stack = [] # type: List[debug_frame_t]
if arg_r.AtEnd(): # -c input, or stdin
script_name = arg_r.Peek() # type: Optional[str]
arg_r.Next()

if script_name is None:
dollar0 = argv0
if flag.c is None:
frame0 = debug_frame.MainEntry('[ stdin ]') # type: debug_frame_t
else:
frame0 = debug_frame.MainEntry('[ -c flag ]')
else:
dollar0 = arg_r.Peek() # the script name

frame0 = debug_frame.Main(dollar0)
debug_stack.append(frame0)
dollar0 = script_name
frame0 = debug_frame.MainFile(script_name)

#log('STACK %s', debug_stack)

script_name = arg_r.Peek() # type: Optional[str]
arg_r.Next()
debug_stack = [frame0]

env_dict = NewDict() # type: Dict[str, value_t]
defaults = NewDict() # type: Dict[str, value_t]
Expand Down Expand Up @@ -943,6 +944,8 @@ def Main(
_AddBuiltinFunc(mem, 'fromJson8', func_misc.FromJson8(True))
_AddBuiltinFunc(mem, 'fromJson', func_misc.FromJson8(False))

_AddBuiltinFunc(mem, 'formatDebugFrame', func_reflect.FormatDebugFrame())

# Demos
_AddBuiltinFunc(mem, '_a2sp', func_misc.BashArrayToSparse())
_AddBuiltinFunc(mem, '_opsp', func_misc.SparseOp())
Expand Down
30 changes: 21 additions & 9 deletions core/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -1314,7 +1314,7 @@ def Dump(self):
# Reuse these immutable objects
t_call = value.Str('Call')
t_source = value.Str('Source')
t_main = value.Str('Main')
t_main = value.Str('MainFile')

for frame in reversed(self.debug_stack):
UP_frame = frame
Expand All @@ -1337,9 +1337,12 @@ def Dump(self):
}
_AddCallToken(d, frame.call_tok)

elif case(debug_frame_e.Main):
frame = cast(debug_frame.Main, UP_frame)
d = {'type': t_main, 'dollar0': value.Str(frame.dollar0)}
elif case(debug_frame_e.MainFile):
frame = cast(debug_frame.MainFile, UP_frame)
d = {
'type': t_main,
'filename': value.Str(frame.main_filename)
}

debug_stack.append(value.Dict(d))
return var_stack, argv_stack, debug_stack
Expand Down Expand Up @@ -2140,9 +2143,12 @@ def GetValue(self, name, which_scopes=scope_e.Shopt):
# bash doesn't tell you the filename sourced
strs.append('source')

elif case2(debug_frame_e.Main):
elif case2(debug_frame_e.MainFile):
strs.append('main') # also bash behavior

else: # ignore
pass

return value.BashArray(strs) # TODO: Reuse this object too?

# $BASH_SOURCE and $BASH_LINENO have OFF BY ONE design bugs:
Expand Down Expand Up @@ -2172,9 +2178,12 @@ def GetValue(self, name, which_scopes=scope_e.Shopt):
# Is this right?
strs.append(frame.source_name)

elif case2(debug_frame_e.Main):
frame = cast(debug_frame.Main, UP_frame)
strs.append(frame.dollar0)
elif case2(debug_frame_e.MainFile):
frame = cast(debug_frame.MainFile, UP_frame)
strs.append(frame.main_filename)

else: # ignore
pass

return value.BashArray(strs) # TODO: Reuse this object too?

Expand All @@ -2191,10 +2200,13 @@ def GetValue(self, name, which_scopes=scope_e.Shopt):
frame = cast(debug_frame.Source, UP_frame)
strs.append(_LineNumber(frame.call_tok))

elif case2(debug_frame_e.Main):
elif case2(debug_frame_e.MainFile):
# Bash does this to line up with 'main'
strs.append('0')

else: # ignore
pass

return value.BashArray(strs) # TODO: Reuse this object too?

elif case('LINENO'):
Expand Down
3 changes: 2 additions & 1 deletion frontend/syntax.asdl
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ module syntax
#
# bash won't print those

Main(str dollar0)
MainEntry(str description) # -c or stdin
| MainFile(str main_filename)
# call_tok => BASH_LINENO
# call_tok may be None with new --source flag?
| Source(Token? call_tok, str source_name)
Expand Down
17 changes: 16 additions & 1 deletion frontend/typed_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
from __future__ import print_function

from _devbuild.gen.runtime_asdl import cmd_value, ProcArgs, Cell
from _devbuild.gen.syntax_asdl import (loc, loc_t, ArgList, command_t, Token)
from _devbuild.gen.syntax_asdl import (loc, loc_t, ArgList, command_t, Token,
debug_frame_t)
from _devbuild.gen.value_asdl import (value, value_e, value_t, RegexMatch, Obj,
cmd_frag, cmd_frag_e, cmd_frag_str,
LiteralBlock)
Expand Down Expand Up @@ -368,6 +369,15 @@ def _ToFrame(self, val):
'Arg %d should be a Frame' % self.pos_consumed,
self.BlamePos())

def _ToDebugFrame(self, val):
# type: (value_t) -> debug_frame_t
if val.tag() == value_e.DebugFrame:
return cast(value.DebugFrame, val).frame

raise error.TypeErr(
val, 'Arg %d should be a DebugFrame' % self.pos_consumed,
self.BlamePos())

def _ToCommandFrag(self, val):
# type: (value_t) -> command_t
if val.tag() == value_e.CommandFrag:
Expand Down Expand Up @@ -491,6 +501,11 @@ def PosFrame(self):
val = self.PosValue()
return self._ToFrame(val)

def PosDebugFrame(self):
# type: () -> debug_frame_t
val = self.PosValue()
return self._ToDebugFrame(val)

def PosCommandFrag(self):
# type: () -> command_t
val = self.PosValue()
Expand Down
14 changes: 14 additions & 0 deletions spec/testdata/debug-frame-lib.ysh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

proc p {
for fr in (vm.getDebugStack()) {
echo $[formatDebugFrame(fr)]
}
}

proc p2 {
p
}

p
echo
p2
2 changes: 2 additions & 0 deletions spec/testdata/debug-frame-main.ysh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

source $_this_dir/debug-frame-lib.ysh
21 changes: 20 additions & 1 deletion spec/ysh-func-builtin.test.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## oils_failures_allowed: 2
## oils_failures_allowed: 3
## our_shell: ysh

#### join()
Expand Down Expand Up @@ -266,3 +266,22 @@ p2
1
2
## END

#### formatDebugFrame()

$[ENV.SH] $[ENV.REPO_ROOT]/spec/testdata/debug-frame-main.ysh

echo

# -c
$[ENV.SH] -c 'source $[ENV.REPO_ROOT]/spec/testdata/debug-frame-lib.ysh'

echo

# stdin
echo 'source $[ENV.REPO_ROOT]/spec/testdata/debug-frame-lib.ysh' | $[ENV.SH]

## STDOUT:
z
## END

0 comments on commit b3ef6f4

Please sign in to comment.