Skip to content

Commit

Permalink
[ASDL] Implement the zero_arg_singleton generate option (#1606)
Browse files Browse the repository at this point in the history
[spec/command-sub] Add case that fails when type_tag is wrong

i.e. when it's not command_e.NoOp

---------

Co-authored-by: Andy C <andy@lenny>
  • Loading branch information
PossiblyAShrub and Andy C committed May 12, 2023
1 parent d531fd5 commit 8d1983b
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 11 deletions.
46 changes: 41 additions & 5 deletions asdl/gen_cpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,8 @@ def VisitSimpleSum(self, sum, name, depth):
def VisitCompoundSum(self, sum, sum_name, depth):
#log('%d variants in %s', len(sum.types), sum_name)

zero_arg_singleton = 'zero_arg_singleton' in sum.generate

# Must fit in 7 bit Obj::type_tag
assert len(sum.types) < 64, 'sum type %r has too many variants' % sum_name

Expand Down Expand Up @@ -368,20 +370,39 @@ def Emit(s, depth=depth):
super_name = '%s_t' % sum_name
tag = 'static_cast<uint16_t>(%s_e::%s)' % (sum_name, variant.name)
class_name = '%s__%s' % (sum_name, variant.name)
singleton = zero_arg_singleton and len(variant.fields) == 0

# NOTE: We disable attribute initialization (really only spids) for
# singleton variants because those variants must be statically
# initialized. The `spids` attribute is an array which is initialized
# during construction for each variant on the gc heap. When the program
# initializes (pre main, ie. _start) we cannot use the gc heap as it has
# yet to initialize. As we do not use the `spids` array on singleton
# variants, we can safely "initialize" `spids` to a nullptr.
self._GenClass(variant, sum.attributes, class_name, [super_name], depth,
tag)
tag, not singleton)

# Generate `extern` declarations for zero arg singleton globals
if zero_arg_singleton:
for variant in sum.types:
if not variant.shared_type and len(variant.fields) == 0:
variant_name = variant.name
Emit('extern GcGlobal<%(sum_name)s__%(variant_name)s> g%(variant_name)s;')

# Allow expr::Const in addition to expr.Const.
Emit('ASDL_NAMES %(sum_name)s {')
for variant in sum.types:
if not variant.shared_type:
variant_name = variant.name
Emit(' typedef %(sum_name)s__%(variant_name)s %(variant_name)s;')
if zero_arg_singleton and len(variant.fields) == 0:
Emit(' static constexpr %(sum_name)s__%(variant_name)s* %(variant_name)s = &g%(variant_name)s.obj;')
else:
Emit(' typedef %(sum_name)s__%(variant_name)s %(variant_name)s;')
Emit('};')
Emit('')

def _GenClass(self, ast_node, attributes, class_name, base_classes, depth,
tag):
tag, init_attributes):
"""For Product and Constructor."""
if base_classes:
bases = ', '.join('public %s' % b for b in base_classes)
Expand Down Expand Up @@ -418,8 +439,14 @@ def FieldInitJoin(strs):
for f in all_fields:
if f in attributes:
# spids are initialized separately

if init_attributes:
value = _DefaultValue(f.typ, conditional=False)
else:
value = 'nullptr'

inits.append('%s(%s)' %
(f.name, _DefaultValue(f.typ, conditional=False)))
(f.name, value))
else:
inits.append('%s(%s)' % (f.name, f.name))

Expand Down Expand Up @@ -486,7 +513,7 @@ def EmitFooter(self):
bases = self._product_bases[name]
#if not bases:
# bases = ['Obj']
self._GenClass(ast_node, attributes, name, bases, depth, tag_num)
self._GenClass(ast_node, attributes, name, bases, depth, tag_num, True)


class MethodDefVisitor(visitor.AsdlVisitor):
Expand Down Expand Up @@ -693,6 +720,15 @@ def VisitSimpleSum(self, sum, name, depth):
def VisitCompoundSum(self, sum, sum_name, depth):
self._EmitStrFunction(sum, sum_name, depth)

# Generate definitions for the for zero arg singleton globals
if 'zero_arg_singleton' in sum.generate:
for variant in sum.types:
if not variant.shared_type and len(variant.fields) == 0:
variant_name = variant.name
self.Emit('GcGlobal<%s__%s> g%s = ' % (sum_name, variant_name, variant_name))
self.Emit(' {{kNotInPool, %s_e::%s, kZeroMask, HeapTag::Global, kIsGlobal}};' % (sum_name, variant_name))
self.Emit('')

for variant in sum.types:
if variant.shared_type:
pass
Expand Down
15 changes: 15 additions & 0 deletions asdl/gen_python.py
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,18 @@ def VisitCompoundSum(self, sum, sum_name, depth):
depth = self.current_depth
self.Emit('')

# Declare any zero argument singleton classes outside of the main
# "namespace" class.
if 'zero_arg_singleton' in sum.generate:
for i, variant in enumerate(sum.types):
# NOTE: Don't generate a class for shared types.
if not variant.shared_type and len(variant.fields) == 0:
# We must use the old-style nameing here, ie. command__NoOp, in order
# to support zero field variants as constants.
class_name = '%s__%s' % (sum_name, variant.name)
self._GenClass(variant, sum.attributes, class_name, (sum_name + '_t',),
i + 1)

# Class that's just a NAMESPACE, e.g. for value.Str
self.Emit('class %s(object):' % sum_name, depth)

Expand All @@ -522,6 +534,9 @@ def VisitCompoundSum(self, sum, sum_name, depth):
if variant.shared_type:
# Don't generate a class.
pass
elif 'zero_arg_singleton' in sum.generate and len(variant.fields) == 0:
self.Emit('%s = %s__%s()' % (variant.name, sum_name, variant.name))
self.Emit('')
else:
# Use fully-qualified name, so we can have osh_cmd.Simple and
# oil_cmd.Simple.
Expand Down
1 change: 1 addition & 0 deletions frontend/syntax.asdl
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ module syntax
| Break | Continue
| Return(expr? value)
attributes (List[int] spids)
generate [zero_arg_singleton]

# Binary(x expr, y expr) or Nullary %Token
# In the first case we have a tag, and an anonymous type.
Expand Down
6 changes: 3 additions & 3 deletions oil_lang/expr_to_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -954,9 +954,9 @@ def func_item(self, node):
self._Suite(node.GetChild(4))
)
elif node.tok.id == Id.Expr_Break:
return command.Break()
return command.Break
elif node.tok.id == Id.Expr_Continue:
return command.Continue()
return command.Continue
elif node.tok.id == Id.Expr_Return:
# 'return' [testlist]
if node.NumChildren() == 1:
Expand All @@ -965,7 +965,7 @@ def func_item(self, node):
return command.Return(self.Expr(node.GetChild(1)))
elif node.tok.id == Id.Expr_Name:
# TODO: turn echo 'hi' into AST
return command.NoOp()
return command.NoOp
else:
raise NotImplementedError(Id_str(node.tok.id))

Expand Down
4 changes: 2 additions & 2 deletions osh/cmd_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from _devbuild.gen.syntax_asdl import (
IntParamBox,
loc, loc_t, loc_e, Token, CompoundWord,
command, command_e, command_t,
command, command_e, command_t, command__NoOp,
condition, condition_e, condition_t,
BraceGroup, ArgList,
assign_op_e,
Expand Down Expand Up @@ -1418,7 +1418,7 @@ def _Dispatch(self, node, cmd_st):
status = self._ExecuteList(node.else_action)

elif case(command_e.NoOp):
node = cast(command.NoOp, UP_node)
node = cast(command__NoOp, UP_node)
status = 0 # make it true

elif case(command_e.Case):
Expand Down
2 changes: 1 addition & 1 deletion osh/cmd_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -2414,7 +2414,7 @@ def ParseCommandSub(self):
self._NewlineOk()

if self.c_kind == Kind.Eof: # e.g. $()
return command.NoOp()
return command.NoOp

c_list = self._ParseCommandTerm()
if len(c_list.children) == 1:
Expand Down
11 changes: 11 additions & 0 deletions spec/command-sub.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -272,3 +272,14 @@ status=1
status=1
## END
## OK bash stdout-json: "\nstatus=0\n\nstatus=0\n"
#### Empty command sub $() (command::NoOp)
# IMPORTANT: catch assert() failure in child process!!!
shopt -s command_sub_errexit
echo -$()- ".$()."
## STDOUT:
-- ..
## END

0 comments on commit 8d1983b

Please sign in to comment.