@@ -17,6 +17,8 @@ use crate::{
1717} ;
1818/// Contains a `GrammarAST` structure produced from a grammar source file.
1919/// As well as any errors which occurred during the construction of the AST.
20+ #[ derive( Debug , Clone ) ]
21+ #[ cfg_attr( test, derive( PartialEq ) ) ]
2022pub struct ASTWithValidityInfo {
2123 yacc_kind : YaccKind ,
2224 ast : GrammarAST ,
@@ -70,6 +72,16 @@ impl ASTWithValidityInfo {
7072 pub fn errors ( & self ) -> & [ YaccGrammarError ] {
7173 self . errs . as_slice ( )
7274 }
75+
76+ #[ allow( unsafe_code) ]
77+ pub unsafe fn set_ast ( & mut self , ast : GrammarAST ) {
78+ self . ast = ast;
79+ }
80+
81+ #[ allow( unsafe_code) ]
82+ pub unsafe fn ast_mut ( & mut self ) -> & mut GrammarAST {
83+ & mut self . ast
84+ }
7385}
7486
7587impl FromStr for ASTWithValidityInfo {
@@ -110,7 +122,8 @@ impl FromStr for ASTWithValidityInfo {
110122/// An AST representing a grammar. This is built up gradually: when it is finished, the
111123/// `complete_and_validate` must be called exactly once in order to finish the set-up. At that
112124/// point, any further mutations made to the struct lead to undefined behaviour.
113- #[ derive( Debug ) ]
125+ #[ derive( Debug , Clone ) ]
126+ #[ cfg_attr( test, derive( PartialEq ) ) ]
114127#[ non_exhaustive]
115128pub struct GrammarAST {
116129 pub start : Option < ( String , Span ) > ,
@@ -140,14 +153,15 @@ pub struct GrammarAST {
140153 pub expect_unused : Vec < Symbol > ,
141154}
142155
143- #[ derive( Debug ) ]
156+ #[ derive( Debug , Clone ) ]
157+ #[ cfg_attr( test, derive( Eq , PartialEq ) ) ]
144158pub struct Rule {
145159 pub name : ( String , Span ) ,
146160 pub pidxs : Vec < usize > , // index into GrammarAST.prod
147161 pub actiont : Option < String > ,
148162}
149163
150- #[ derive( Debug ) ]
164+ #[ derive( Debug , Clone ) ]
151165#[ cfg_attr( test, derive( Eq , PartialEq ) ) ]
152166pub struct Production {
153167 pub symbols : Vec < Symbol > ,
@@ -195,7 +209,7 @@ impl fmt::Display for Symbol {
195209}
196210
197211impl GrammarAST {
198- pub fn new ( ) -> GrammarAST {
212+ pub ( crate ) fn new ( ) -> GrammarAST {
199213 GrammarAST {
200214 start : None ,
201215 rules : IndexMap :: new ( ) , // Using an IndexMap means that we retain the order
@@ -772,4 +786,44 @@ mod test {
772786 . contains( & ast_validity. ast( ) . tokens. get_index_of( "b" ) . unwrap( ) )
773787 ) ;
774788 }
789+
790+ #[ test]
791+ #[ allow( unsafe_code) ]
792+ fn clone_ast_changing_start_rule ( ) {
793+ use super :: * ;
794+ use crate :: yacc:: * ;
795+ let y_src_contents = r#"
796+ %token A B C
797+ %%
798+ AStart: A ':' BStart ';';
799+ BStart: B ',' C | C ',' B;
800+ "# ;
801+
802+ let astart = format ! ( "%start AStart\n {y_src_contents}" ) ;
803+ let bcstart = format ! ( "%start BStart\n {y_src_contents}" ) ;
804+
805+ let mut astart_ast_validity = ASTWithValidityInfo :: new (
806+ YaccKind :: Original ( YaccOriginalActionKind :: NoAction ) ,
807+ & astart,
808+ ) ;
809+ let bstart_ast_validity = ASTWithValidityInfo :: new (
810+ YaccKind :: Original ( YaccOriginalActionKind :: NoAction ) ,
811+ & bcstart,
812+ ) ;
813+
814+ assert ! ( astart_ast_validity. is_valid( ) ) ;
815+ assert ! ( bstart_ast_validity. is_valid( ) ) ;
816+
817+ // We'll take the `astart` grammar and programatically modify it into the `bcstart` one.
818+ let modified_astart = unsafe { astart_ast_validity. ast_mut ( ) } ;
819+
820+ let start_len = "%start " . len ( ) ;
821+ modified_astart. start = Some ( (
822+ "BStart" . to_string ( ) ,
823+ Span :: new ( start_len, start_len + "BStart" . len ( ) ) ,
824+ ) ) ;
825+ // This equality test is really a hack since it relies on all the start rules having the same str length
826+ // So as not to change the spans of any rules after the initial `%start` decl.
827+ assert_eq ! ( astart_ast_validity, bstart_ast_validity) ;
828+ }
775829}
0 commit comments