blob: 04f39c7f52d727aef68d091d9e20ceb878a1ead2 [file] [log] [blame]
/*
* parser.cup - Java CUP file containing LALR(1) grammar for DASM
*/
package dasm;
import java_cup.runtime.*;
import dasm.tokens.number_token;
import dasm.tokens.relative_number_token;
import dasm.tokens.variant_token;
action code {:
int access_val;
public DAsm dAsm;
public Scanner scanner;
public boolean isInterface;
:};
parser code {:
public boolean isInterface;
public Scanner scanner;
public DAsm dAsm;
public void report_error(String message, Object info) {
if(info != null)
dAsm.report_error("Warning: " + message + "(" + info.toString() + ")");
else
dAsm.report_error("Warning: " + message);
}
public void report_fatal_error(String message, Object info) {
if(info != null)
dAsm.report_error("Error: " + message + "(" + info.toString() + ")");
else
dAsm.report_error("Error: " + message);
}
parser(DAsm dAsm, Scanner scanner) {
super();
this.scanner = scanner;
this.dAsm = dAsm;
}
:};
init with {:
action_obj.scanner = scanner;
action_obj.dAsm = dAsm;
:};
scan with {:
return scanner.next_token();
:};
/* Terminals (tokens returned by the scanner). */
terminal token
// Directives (words beginning with a '.')
DCATCH, DCLASS, DEND, DFIELD, DLIMIT, DLINE, DMETHOD, DSET, DSUPER,
DSOURCE, DTHROWS, DVAR, DIMPLEMENTS, DINTERFACE, DBYTECODE, DDEBUG,
DENCLOSING, DSIGNATURE, DATTRIBUTE, DDEPRECATED, DINNER,
DANNOTATION,
// keywords for directives
USING, IS, FROM, METHOD, SIGNATURE, REGS, FIELD, CLASS,
TO, INNER, OUTER, VISIBLE, INVISIBLE, VISIBLEPARAM, INVISIBLEPARAM,
// access types
ABSTRACT, FINAL, INTERFACE, NATIVE, PRIVATE, PROTECTED, PUBLIC, STATIC,
SYNCHRONIZED, DECLARED_SYNCHRONIZED, TRANSIENT, VOLATILE,
// added these for java 1.5 compliance :
ANNOTATION, ENUM, BRIDGE, VARARGS, STRICT, SYNTHETIC,
// complex instructions
FILL_ARRAY_DATA, FILL_ARRAY_DATA_END,
PACKED_SWITCH, PACKED_SWITCH_END,
SPARSE_SWITCH, SPARSE_SWITCH_END,
DEFAULT,
// special symbols
EQ, SEP, COLON
;
terminal str_token Str, Word, Insn;
terminal int_token Int;
terminal number_token Num;
terminal relative_number_token Relative;
non terminal str_token classname, inner_name, inner_inner, inner_outer, optional_signature;
non terminal variant_token optional_default, item, any_item;
/* Non terminals */
non terminal symbol
access_item, access_items, access_list, catch_expr, class_spec,
complex_instruction, defmethod, directive, endmethod, field_list,
field_spec, fields, instruction, implements, implements_list, implements_spec,
dasm_file, dasm_file_classes, dasm_file_class, label, limit_expr,
method_list,
method_spec, methods, set_expr, simple_instruction, source_spec,
statement, statements, stmnt, super_spec, line_expr,
throws_expr, var_expr, dex_version_spec,
enclosing_spec, signature_spec, signature_expr, dasm_header,
deprecated_spec, deprecated_expr,
generic_attributes, generic_list, generic_spec, generic_expr,
field_start, endfield, field_exts, field_ext_list, field_ext_expr,
inners, inner_list, inner_spec,
fa_data, fa_data_args, fa_data_list, fa_data_entry, fa_data_end,
ps_table, ps_table_args, ps_table_list, ps_table_entry, ps_table_end,
ss_table, ss_table_args, ss_table_list, ss_table_entry, ss_table_end,
// used for Annotation attributes :
annotations, ann_cls_list, ann_cls_spec, endannotation, ann_cls_expr,
ann_clf_expr, ann_met_expr, ann_arglist, ann_arg_list, ann_arg_spec,
ann_def_spec, ann_def_val, ann_value_items, ann_value, ann_def_expr,
ann_arg_expr, ann_nest, endannotationsep, ann_ann_value, ann_ann_list,
ann_value_list
;
non terminal int_token access;
/* The grammar */
dasm_file ::= dasm_file_classes;
dasm_file_classes ::= dasm_file_classes dasm_file_class | dasm_file_class;
dasm_file_class ::=
dasm_header
inners
fields
methods
;
dasm_header ::=
dex_version_spec
source_spec
class_spec
super_spec
implements
signature_spec
enclosing_spec
deprecated_spec
annotations
generic_attributes
{: dAsm.endHeader(); :}
;
/* ========== Dex version specification ========== */
dex_version_spec ::=
DBYTECODE Num:n SEP
{: dAsm.setVersion(n.number_val); :}
|
/* nothing */
;
/* ========== Source specification ========== */
source_spec ::=
DSOURCE Str:s SEP
{: dAsm.setSource(s.str_val); :}
|
DSOURCE Word:w SEP
{: dAsm.setSource(w.str_val); :}
|
/* nothing */
;
/* ========== Class specification ========== */
class_spec ::=
DCLASS access:a classname:name SEP
{: isInterface = false;
dAsm.setClass(name.str_val,
(a.int_val)); :}
|
DINTERFACE access:a classname:name SEP
{: isInterface = true;
dAsm.setClass(name.str_val,
(a.int_val |
com.android.dx.rop.code.AccessFlags.ACC_INTERFACE)); :}
;
classname ::= Word:w
{: RESULT.str_val = Utils.convertDotsToSlashes(w.str_val); :}
;
access ::=
{: access_val = 0; :}
access_list
{: RESULT.int_val = access_val; :}
;
access_list ::= access_items | ;
access_items ::= access_items access_item | access_item ;
access_item ::=
PUBLIC {: access_val |= com.android.dx.rop.code.AccessFlags.ACC_PUBLIC; :}
|
PRIVATE {: access_val |= com.android.dx.rop.code.AccessFlags.ACC_PRIVATE; :}
|
PROTECTED {: access_val |= com.android.dx.rop.code.AccessFlags.ACC_PROTECTED; :}
|
STATIC {: access_val |= com.android.dx.rop.code.AccessFlags.ACC_STATIC; :}
|
FINAL {: access_val |= com.android.dx.rop.code.AccessFlags.ACC_FINAL; :}
|
SYNCHRONIZED {: access_val |= com.android.dx.rop.code.AccessFlags.ACC_SYNCHRONIZED; :}
|
VOLATILE {: access_val |= com.android.dx.rop.code.AccessFlags.ACC_VOLATILE; :}
|
TRANSIENT {: access_val |= com.android.dx.rop.code.AccessFlags.ACC_TRANSIENT; :}
|
NATIVE {: access_val |= com.android.dx.rop.code.AccessFlags.ACC_NATIVE; :}
|
INTERFACE {: access_val |= com.android.dx.rop.code.AccessFlags.ACC_INTERFACE; :}
|
ABSTRACT {: access_val |= com.android.dx.rop.code.AccessFlags.ACC_ABSTRACT; :}
|
ANNOTATION {: access_val |= com.android.dx.rop.code.AccessFlags.ACC_ANNOTATION; :}
|
ENUM {: access_val |= com.android.dx.rop.code.AccessFlags.ACC_ENUM; :}
|
BRIDGE {: access_val |= com.android.dx.rop.code.AccessFlags.ACC_BRIDGE; :}
|
VARARGS {: access_val |= com.android.dx.rop.code.AccessFlags.ACC_VARARGS; :}
|
STRICT {: access_val |= com.android.dx.rop.code.AccessFlags.ACC_STRICT; :}
|
SYNTHETIC {: access_val |= com.android.dx.rop.code.AccessFlags.ACC_SYNTHETIC; :}
|
DECLARED_SYNCHRONIZED {: access_val |= com.android.dx.rop.code.AccessFlags.ACC_DECLARED_SYNCHRONIZED; :}
;
/* ========== Superclass specification ========== */
super_spec ::=
DSUPER classname:name SEP
{: dAsm.setSuperClass(name.str_val); :}
|
/* nothing */
{: if(isInterface == false)
dAsm.setSuperClass("java/lang/Object");
else
// Dalvik requires interfaces to have superclass
dAsm.setSuperClass("java/lang/Object"); :}
;
/* ========== Implements specification ========== */
implements ::= implements_list | /* empty */ ;
implements_list ::= implements_list implements_spec | implements_spec ;
implements_spec ::= DIMPLEMENTS classname:name SEP
{: dAsm.addInterface(name.str_val); :}
;
/* ========== Signature specification ========== */
signature_spec ::=
DSIGNATURE signature_expr SEP
|
/* nothing */
;
signature_expr ::= Str:sig
{: dAsm.setSignature(sig.str_val); :}
;
/* ========== EnclosingMethod attribute specification ========== */
enclosing_spec ::=
DENCLOSING METHOD Word:w SEP
{: dAsm.setEnclosingMethod(w.str_val); :}
|
/* nothing */
;
/* ========== Deprecated attribute ========== */
deprecated_spec ::=
DDEPRECATED deprecated_expr SEP
|
/* nothing */
;
deprecated_expr ::=
{: /*dAsm.setDeprecated();*/dAsm.report_error("WARNING: @deprecated is not supported"); :}
;
/* ========== Annotation specification ========== */
annotations ::= ann_cls_list |
// empty
;
ann_cls_list ::= ann_cls_list ann_cls_spec | ann_cls_spec ;
ann_cls_spec ::= ann_cls_expr ann_arglist endannotationsep ;
endannotationsep ::= endannotation SEP ;
endannotation ::= DEND ANNOTATION
{: //TODO: NOT SUPPORTED dAsm.endAnnotation();
dAsm.report_error("WARNING: Annotations are not supported"); :}
;
ann_cls_expr ::= DANNOTATION ann_clf_expr ;
ann_clf_expr ::=
VISIBLE classname:name SEP
{: //TODO: NOT SUPPORTED dAsm.addAnnotation(true, name.str_val);
dAsm.report_error("WARNING: Annotations are not supported"); :}
|
INVISIBLE classname:name SEP
{: //TODO: NOT SUPPORTED dAsm.addAnnotation(false, name.str_val);
dAsm.report_error("WARNING: Annotations are not supported"); :}
;
ann_met_expr ::=
VISIBLE classname:name SEP
{: //TODO: NOT SUPPORTED dAsm.addAnnotation(true, name.str_val);
dAsm.report_error("WARNING: Annotations are not supported"); :}
|
INVISIBLE classname:name SEP
{: //TODO: NOT SUPPORTED dAsm.addAnnotation(false, name.str_val);
dAsm.report_error("WARNING: Annotations are not supported"); :}
|
VISIBLEPARAM Int:n classname:name SEP
{: //TODO: NOT SUPPORTED dAsm.addAnnotation(true, name.str_val, n.int_val);
dAsm.report_error("WARNING: Annotations are not supported"); :}
|
INVISIBLEPARAM Int:n classname:name SEP
{: //TODO: NOT SUPPORTED dAsm.addAnnotation(false, name.str_val, n.int_val);
dAsm.report_error("WARNING: Annotations are not supported"); :}
;
ann_arglist ::= ann_arg_list |
// empty
;
ann_arg_list ::= ann_arg_list ann_arg_spec | ann_arg_spec ;
ann_arg_spec ::= ann_arg_expr EQ ann_value_list ;
ann_arg_expr ::=
Word:n Word:dsc
{: //TODO: NOT SUPPORTED dAsm.addAnnotationField(n.str_val, dsc.str_val, null);
dAsm.report_error("WARNING: Annotations are not supported"); :}
|
Word:n Word:dsc Word:sub
{: //TODO: NOT SUPPORTED dAsm.addAnnotationField(n.str_val, dsc.str_val, sub.str_val);
dAsm.report_error("WARNING: Annotations are not supported"); :}
;
ann_def_spec ::= DEFAULT SEP
{: //TODO: NOT SUPPORTED dAsm.addAnnotation();
dAsm.report_error("WARNING: Annotations are not supported"); :}
;
ann_value_list ::= ann_value_items SEP | ann_ann_list ;
ann_value_items ::= ann_value_items ann_value | ann_value ;
ann_value ::= any_item:v
{: //TODO: NOT SUPPORTED dAsm.addAnnotationValue(v.variant_val);
dAsm.report_error("WARNING: Annotations are not supported"); :}
;
ann_ann_list ::= ann_ann_list ann_ann_value | ann_ann_value ;
ann_ann_value ::= DANNOTATION ann_nest ann_arglist endannotationsep ;
ann_nest ::= SEP
{: //TODO: NOT SUPPORTED dAsm.nestAnnotation();
dAsm.report_error("WARNING: Annotations are not supported"); :}
;
ann_def_val ::= ann_def_expr EQ ann_value_list ;
ann_def_expr ::=
Word:dsc
{: //TODO: NOT SUPPORTED dAsm.addAnnotationField(null, dsc.str_val, null);
dAsm.report_error("WARNING: Annotations are not supported"); :}
|
Word:dsc Word:sub
{: //TODO: NOT SUPPORTED dAsm.addAnnotationField(null, dsc.str_val, sub.str_val);
dAsm.report_error("WARNING: Annotations are not supported"); :}
;
/* ========== Generic attributes specification ========== */
generic_attributes ::= generic_list | /* empty */ ;
generic_list ::= generic_list generic_spec | generic_spec ;
generic_spec ::= DATTRIBUTE generic_expr SEP ;
generic_expr ::= Word:name Str:file
{: dAsm.addGenericAttr(name.str_val, file.str_val); :}
;
/* ========== Fields ========== */
fields ::= field_list | /* empty */ ;
field_list ::= field_list field_spec | field_spec ;
field_spec ::=
DFIELD access:a Word:name Word:desc SIGNATURE Str:sig optional_default:v SEP
{: dAsm.addField((short)a.int_val, name.str_val, desc.str_val,
sig.str_val, v.variant_val); :}
|
DFIELD access:a Word:name Word:desc optional_default:v SEP
{: dAsm.addField((short)a.int_val, name.str_val, desc.str_val,
null, v.variant_val); :}
|
DFIELD field_start field_exts endfield
;
// default value for a field
optional_default ::=
EQ item:v
{: RESULT.variant_val = v.variant_val; :}
|
/* empty */
{: RESULT.variant_val = null; :}
;
// multiline form of field description
field_start ::= access:a Word:name Word:desc optional_default:v SEP
{: dAsm.beginField((short)a.int_val, name.str_val,
desc.str_val, v.variant_val); :}
;
endfield ::= DEND FIELD SEP
{: dAsm.endField(); :}
;
field_exts ::= field_ext_list | /* empty */ ;
field_ext_list ::= field_ext_list field_ext_expr | field_ext_expr ;
field_ext_expr ::=
DSIGNATURE signature_expr SEP
|
DATTRIBUTE generic_expr SEP
|
DDEPRECATED deprecated_expr SEP
|
DANNOTATION ann_clf_expr ann_arglist endannotationsep
;
// an item is an integer, a float/double/long, or a quoted string
item ::=
Int:i {: RESULT.variant_val = new Integer(i.int_val); :}
|
Num:n {: RESULT.variant_val = n.number_val; :}
|
Str:s {: RESULT.variant_val = s.str_val; :}
;
// an item is any possible type
any_item ::=
Word:w {: RESULT.variant_val = w.str_val; :} // for enum
|
item:v {: RESULT.variant_val = v.variant_val; :}
;
/* ========== Inner classes ========== */
inners ::= inner_list |
// empty
;
inner_list ::= inner_list inner_spec | inner_spec ;
inner_spec ::=
DINNER CLASS access:a inner_name:n inner_inner:i inner_outer:o SEP
{: dAsm.addInner((short)a.int_val,
n.str_val, i.str_val, o.str_val); :}
|
DINNER INTERFACE access:a inner_name:n inner_inner:i inner_outer:o SEP
{: dAsm.addInner((short)(a.int_val |
com.android.dx.rop.code.AccessFlags.ACC_INTERFACE),
n.str_val, i.str_val, o.str_val); :}
;
inner_name ::=
Word:w
{: RESULT.str_val = w.str_val; :}
|
// empty
{: RESULT.str_val = null; :}
;
inner_inner ::=
INNER classname:w
{: RESULT.str_val = w.str_val; :}
|
// empty
{: RESULT.str_val = null; :}
;
inner_outer ::=
OUTER classname:w
{: RESULT.str_val = w.str_val; :}
|
// empty
{: RESULT.str_val = null; :}
;
/* ========== Methods ========== */
methods ::= method_list | /* empty */;
method_list ::= method_list method_spec | method_spec ;
method_spec ::=
defmethod
statements
endmethod
|
defmethod endmethod
;
defmethod ::=
DMETHOD access:i Word:name SEP
{: String split[] = Utils.getMethodSignatureFromString(name.str_val);
dAsm.newMethod(split[0], split[1], i.int_val); :}
;
endmethod ::=
DEND METHOD SEP
{: dAsm.endMethod(); :}
;
/* ========== Statements in a method ========== */
statements ::= statements statement | statement ;
statement ::=
{: dAsm.setLine(scanner.token_line_num); :}
stmnt SEP
;
stmnt ::=
instruction
|
directive
|
error
|
label
|
/* empty */
;
// label:
label ::=
Word:label COLON
{: dAsm.plantLabel(label.str_val); :}
|
Int:label COLON instruction
{: dAsm.plantLabel(String.valueOf(label.int_val)); :}
;
// Directives (.catch, .set, .limit, etc.)
directive ::=
DVAR var_expr
|
DLIMIT limit_expr
|
DLINE line_expr
|
DTHROWS throws_expr
|
DCATCH catch_expr
|
DSET set_expr
|
DSIGNATURE signature_expr
|
DATTRIBUTE generic_expr
|
DDEPRECATED deprecated_expr
|
DANNOTATION ann_met_expr ann_arglist endannotation
|
DANNOTATION ann_def_spec ann_def_val endannotation
;
//
// .var <num> is <name> <desc> from StartLab to EndLab
// .var <num> is <name> <desc> signature <sign> from StartLab to EndLab
//
var_expr ::=
Int:reg IS Word:name Word:desc optional_signature:sign FROM Word:slab TO Word:elab
{: dAsm.addVar(slab.str_val, elab.str_val, name.str_val,
desc.str_val, sign.str_val, reg.int_val); :}
|
Int:reg IS Word:name Word:desc optional_signature:sign
{: dAsm.addVar(null, null, name.str_val, desc.str_val,
sign.str_val, reg.int_val); :}
|
Int:reg IS Word:name Word:desc optional_signature:sign FROM Int:soff TO Int:eoff
{: dAsm.addVar(soff.int_val, eoff.int_val, name.str_val,
desc.str_val, sign.str_val, reg.int_val); :}
;
// optional signature specification for a .var
optional_signature ::=
SIGNATURE Str:s
{: RESULT.str_val = s.str_val; :}
|
/* empty */
{: RESULT.str_val = null; :}
;
// .limit regs <val>
limit_expr ::=
REGS Int:v // .limit regs
{: dAsm.setRegsSize(v.int_val); :}
|
Word:w Int:v
{: dAsm.report_error(".limit expected \"regs\" , but got "
+ w.str_val); :}
;
// .line <num>
line_expr ::= Int:v
{: dAsm.addLine(v.int_val); :}
;
// .throws <class>
throws_expr ::= classname:s
{: dAsm.addThrow(s.str_val); :}
;
// .catch <class> from <label1> to <label2> using <branchlab>
catch_expr ::=
classname:aclass FROM Word:fromlab TO Word:tolab USING Word:branchlab
{: dAsm.addCatch(aclass.str_val,
fromlab.str_val,
tolab.str_val,
branchlab.str_val); :}
|
classname:aclass FROM Int:fromoff TO Int:tooff USING Int:branchoff
{: dAsm.addCatch(aclass.str_val,
fromoff.int_val,
tooff.int_val,
branchoff.int_val); :}
;
// .set <var> = <val>
set_expr ::=
Word:name any_item:v
{: scanner.dict.put(name.str_val, v); :}
;
instruction ::=
simple_instruction
|
complex_instruction
;
// Various patterns of instruction:
// instruction [<pattern>]
simple_instruction ::=
/* Format: 10x */
Insn:i
{: dAsm.addOpcode(i.str_val); :}
|
/* Format: 11x, 10t, 20t, 30t */
Insn:i Word:n
{: dAsm.addOpcode(i.str_val, n.str_val); :}
|
/* Format: relative 10t, 20t, 30t */
Insn:i Relative:n
{: dAsm.addRelativeGoto(i.str_val, n.int_val); :}
|
/* Format: 11n, 21s, 31i, 21h, 51l */
Insn:i Word:n1 Num:n2
{: dAsm.addOpcode(i.str_val, n1.str_val, n2.number_val); :}
|
/* Format: same as above. Handles the case when last argument is integer */
Insn:i Word:n1 Int:n2
{: dAsm.addOpcode(i.str_val, n1.str_val, new Integer(n2.int_val)); :}
|
/* Format: 12x, 22x, 32x, 21t, 21c (string@, type@), 31c, 35c, 3rc */
Insn:i Word:n1 Word:n2
{: dAsm.addOpcode(i.str_val, n1.str_val, n2.str_val); :}
|
/* Format: relative 21t */
Insn:i Word:n1 Relative:n2
{: dAsm.addRelativeGoto(i.str_val, n1.str_val, n2.int_val); :}
|
/* Format: 23x, 22t, 21c (field@), 22c */
Insn:i Word:n1 Word:n2 Word:n3
{: dAsm.addOpcode(i.str_val, n1.str_val, n2.str_val, n3.str_val); :}
|
/* Format: relative 22t */
Insn:i Word:n1 Word:n2 Relative:n3
{: dAsm.addRelativeGoto(i.str_val, n1.str_val, n2.str_val, n3.int_val); :}
|
/* Format: 22b, 22s */
Insn:i Word:n1 Word:n2 Int:n3
{: dAsm.addOpcode(i.str_val, n1.str_val, n2.str_val, n3.int_val); :}
|
/* Format: 21c (string@) */
Insn:i Word:n1 Str:n2
{: dAsm.addOpcode(i.str_val, n1.str_val, n2.str_val); :}
|
/* Format: 22c (field@)*/
Insn:i Word:n1 Word:n2 Word:n3 Word:n4
{: dAsm.addOpcode(i.str_val, n1.str_val, n2.str_val, n3.str_val, n4.str_val); :}
;
// complex (multiline) instructions
// fill-array-data <data>
// packed-switch <table>
// sparse-switch <table>
complex_instruction ::=
FILL_ARRAY_DATA fa_data
|
PACKED_SWITCH ps_table
|
SPARSE_SWITCH ss_table
;
// fill-array-data register type
// <value1>
// ...
// <valueN>
// fill-array-data-end
fa_data ::=
fa_data_args
fa_data_list
fa_data_end
;
fa_data_args ::=
Word:r Word:t SEP // <register> <type>
{: dAsm.newFillArrayData(r.str_val, t.str_val); :}
;
fa_data_list ::= fa_data_list fa_data_entry | fa_data_entry ;
fa_data_entry ::=
Num:data SEP
{: dAsm.addFillArrayData(data.number_val); :}
|
Int:data SEP
{: dAsm.addFillArrayData(new Integer(data.int_val)); :}
;
fa_data_end ::=
FILL_ARRAY_DATA_END
{: dAsm.endFillArrayData(); :}
;
// packed-switch register first_key
// <target1>
// ...
// <targetN>
// packed-switch-end
ps_table ::=
ps_table_args
ps_table_list
ps_table_end
;
ps_table_args ::=
Word:r Int:k SEP // <register> <first_key>
{: dAsm.newPackedSwitch(r.str_val, k.int_val); :}
;
ps_table_list ::= ps_table_list ps_table_entry | ps_table_entry ;
ps_table_entry ::=
Word:target SEP
{: dAsm.addPackedSwitchData(target.str_val); :}
|
Relative:target SEP
{: dAsm.addPackedSwitchData(target.int_val); :}
;
ps_table_end ::=
PACKED_SWITCH_END
{: dAsm.endSwitch(); :}
;
// sparse-switch register
// <value> : <label>
// <value> : <label>
// ...
// sparse-switch-end
ss_table ::=
ss_table_args
ss_table_list
ss_table_end
;
ss_table_args ::=
Word:r SEP // <register>
{: dAsm.newSparseSwitch(r.str_val); :}
;
ss_table_list ::= ss_table_list ss_table_entry | ss_table_entry ;
ss_table_entry ::=
Int:i COLON Word:w SEP
{: dAsm.addSparseSwitchData(i.int_val, w.str_val); :}
|
Int:i COLON Relative:off SEP
{: dAsm.addSparseSwitchData(i.int_val, off.int_val); :}
;
ss_table_end ::=
SPARSE_SWITCH_END
{: dAsm.endSwitch(); :}
;