2021-05-14 16:51:04 -04:00
# include <map>
2021-05-14 11:47:54 -04:00
# include "NVL.h"
2021-05-14 16:51:04 -04:00
namespace {
2021-05-16 12:59:18 -04:00
// general helpers, may move these into a different translation unit
2021-05-16 13:28:40 -04:00
bool str_test_every_char_not ( int ( * test ) ( int ) , std : : string str )
2021-05-15 17:48:37 -04:00
{
2021-05-16 12:59:18 -04:00
for ( auto & x : str )
{
2021-05-16 13:28:40 -04:00
if ( ! ( * test ) ( x ) )
2021-05-16 12:59:18 -04:00
return false ;
}
return true ;
}
void indent_loop ( int indent )
{
for ( int i = 0 ; i < indent ; i + + )
{
std : : cout < < " \t " ;
}
2021-05-15 17:48:37 -04:00
}
2021-05-19 17:02:57 -04:00
int query_ifstream_line_number ( std : : ifstream & stream )
{
std : : streampos pos = stream . tellg ( ) ;
stream . seekg ( 0 ) ; // we should not be eof
int i = 0 ;
char c { } ;
while ( stream . tellg ( ) < pos )
{
if ( c = = ' \n ' )
i + + ;
stream . get ( c ) ;
}
return i ;
}
2021-05-15 17:48:37 -04:00
2021-05-16 12:59:18 -04:00
// facilities for scopers
2021-05-14 16:51:04 -04:00
const std : : map < char , char > SCOPER_MAP = {
{ ' { ' , ' } ' } , // CURLY
{ ' < ' , ' > ' } , // ANGLED
{ ' ( ' , ' ) ' } , // BRACKET
{ ' [ ' , ' ] ' } , // SQUARE
{ ' \' ' , ' \' ' } , // SQUOTE
{ ' \" ' , ' \" ' } // DQUOTE
} ;
bool need_escape ( char c )
{
2021-05-15 17:48:37 -04:00
// todo: refactor, too cryptic
2021-05-14 16:51:04 -04:00
return ( c = = ' \' ' | | c = = ' \" ' ) ;
}
struct ScopeContext {
std : : vector < char > Scope ;
} ;
2021-05-15 17:48:37 -04:00
std : : streampos scope_cope ( char scope_char , std : : ifstream & stream )
2021-05-14 11:47:54 -04:00
{
2021-05-15 17:48:37 -04:00
// important
// scope_cope() expects the scope opener to already have been fetched and stored in scope_char
// i.o.w. it wont be able to cope properly because there will be one extra level of scope that it cannot match
2021-05-14 16:51:04 -04:00
std : : streampos initial = stream . tellg ( ) ;
std : : streampos final ;
char c { } ;
ScopeContext current_scope ;
current_scope . Scope . push_back ( scope_char ) ;
while ( ! current_scope . Scope . empty ( ) ) {
stream . get ( c ) ;
2021-05-14 23:17:55 -04:00
2021-05-15 17:48:37 -04:00
// push scope if opening scoper is found
2021-05-15 01:51:02 -04:00
bool set_scope_on_this_iter = false ;
2021-05-15 17:54:24 -04:00
// there cannot be any more scope inside of quoted strings
if ( current_scope . Scope . back ( ) ! = ' \' ' & & current_scope . Scope . back ( ) ! = ' \" ' ) {
2021-05-14 23:17:55 -04:00
for ( auto const & x : SCOPER_MAP )
{
if ( c = = x . first ) {
2021-05-15 01:51:02 -04:00
current_scope . Scope . push_back ( c ) ;
set_scope_on_this_iter = true ;
2021-05-14 23:17:55 -04:00
break ;
}
2021-05-14 16:51:04 -04:00
}
}
2021-05-15 17:48:37 -04:00
// pop last scope if ending scoper is found
// set_scope_on_this_iter to prevent immediately popping scopers that have the same closers and openers
2021-05-15 01:51:02 -04:00
if ( ! set_scope_on_this_iter & & c = = SCOPER_MAP . at ( current_scope . Scope . back ( ) ) )
current_scope . Scope . pop_back ( ) ;
2021-05-14 16:51:04 -04:00
if ( c = = ' \\ ' ) // encounters an escaped sequence
{
char cc = stream . peek ( ) ;
if ( need_escape ( cc ) ) // see if the escaped char happens to be a scoper
{
stream . get ( c ) ; // skip it, since whoever wrote the script escaped it
}
}
2021-05-19 17:02:57 -04:00
// cannot match all the scopes and has read to the end of the file
2021-05-14 16:51:04 -04:00
if ( stream . eof ( ) )
2021-05-19 17:02:57 -04:00
throw std : : runtime_error ( " Can't cope with scope at line " + std : : to_string ( query_ifstream_line_number ( stream ) ) ) ;
2021-05-14 16:51:04 -04:00
}
2021-05-14 11:47:54 -04:00
2021-05-14 16:51:04 -04:00
final = stream . tellg ( ) ;
stream . seekg ( initial ) ;
2021-05-15 01:51:02 -04:00
2021-05-14 16:51:04 -04:00
return final ;
2021-05-14 11:47:54 -04:00
}
2021-05-16 12:59:18 -04:00
// facilities for parsing
2021-05-16 13:28:40 -04:00
void skip_ws ( std : : ifstream & stream )
{
stream > > std : : ws ;
}
2021-05-19 17:02:57 -04:00
bool only_ws_before_next_newline ( std : : ifstream & stream ) {
if ( ! isspace ( stream . peek ( ) ) )
return false ;
std : : streampos initial = stream . tellg ( ) ;
char c { } ;
stream . get ( c ) ;
while ( c ! = ' \n ' )
{
stream . get ( c ) ;
if ( ! isspace ( c ) ) {
stream . seekg ( initial ) ;
return false ;
}
}
stream . seekg ( initial ) ;
return true ;
}
2021-05-16 13:28:40 -04:00
2021-05-14 20:44:09 -04:00
std : : string read_sequence_name ( std : : ifstream & nvl )
2021-05-14 11:47:54 -04:00
{
2021-05-15 17:48:37 -04:00
// this function will move the reading head of nvl, this is hard to keep track of but simplifies the program
skip_ws ( nvl ) ;
2021-05-14 16:51:04 -04:00
std : : string token ;
2021-05-15 17:48:37 -04:00
char c { } ;
nvl . get ( c ) ;
while ( c ! = ' ' & & c ! = ' { ' & & c ! = ' \n ' )
2021-05-14 16:51:04 -04:00
{
2021-05-15 17:48:37 -04:00
token + = c ;
nvl . get ( c ) ;
2021-05-14 16:51:04 -04:00
}
2021-05-15 17:48:37 -04:00
nvl . putback ( c ) ;
2021-05-14 16:51:04 -04:00
return token ;
}
2021-05-15 13:38:10 -04:00
2021-05-19 17:02:57 -04:00
void skip_comment ( std : : ifstream & nvl , char & c )
2021-05-15 13:38:10 -04:00
{
if ( nvl . peek ( ) = = ' ; ' )
{
nvl . get ( c ) ;
2021-05-15 17:48:37 -04:00
while ( c ! = ' \n ' )
{
nvl . get ( c ) ;
}
skip_ws ( nvl ) ;
2021-05-19 17:02:57 -04:00
skip_comment ( nvl , c ) ;
2021-05-15 17:48:37 -04:00
skip_ws ( nvl ) ;
}
}
2021-05-14 16:51:04 -04:00
}
namespace NVL
{
2021-05-19 17:02:57 -04:00
void parse_Dialogue ( Context old_context , std : : ifstream & nvl )
{
if ( std : : get < Call * > ( old_context . Scope_Hierarchy . back ( ) ) - > Objects . size ( ) ! = 1 )
throw std : : runtime_error ( " Failed to parse dialogue at line " + std : : to_string ( query_ifstream_line_number ( nvl ) ) ) ;
const Object say_command = Object ( " Say " ) ;
Object speaker = std : : get < Call * > ( old_context . Scope_Hierarchy . back ( ) ) - > Objects . front ( ) ; // Copy speaker object
Sequence * parent_sequence = std : : get < Sequence * > ( old_context . Scope_Hierarchy . rbegin ( ) [ 1 ] ) ;
// old call has never been pushed, we need to exit parse_Call right away when we finish parsing the dialogue, because this already deals with calls
char c { } ;
nvl . get ( c ) ; // get <
std : : streampos end = scope_cope ( ' < ' , nvl ) ;
skip_ws ( nvl ) ;
do
{
parent_sequence - > Calls . push_back ( Call ( ) ) ;
parent_sequence - > Calls . back ( ) . Objects . push_back ( say_command ) ;
parent_sequence - > Calls . back ( ) . Objects . push_back ( speaker ) ;
std : : string text ;
if ( nvl . peek ( ) = = ' > ' )
break ;
// im not sure if the execution order here will be problematic, seems to work ok
while ( ! ( ( c = = ' \n ' & & only_ws_before_next_newline ( nvl ) ) | | nvl . peek ( ) = = ' > ' ) )
{
nvl . get ( c ) ;
text + = c ;
}
while ( isspace ( text . back ( ) ) )
text . erase ( text . size ( ) - 1 , 1 ) ;
for ( size_t i = text . find ( ' \n ' ) ; i ! = std : : string : : npos ; i = text . find ( ' \n ' ) )
{
int e = 0 ;
std : : cout < < text . size ( ) ;
while ( isspace ( text . at ( i + e ) ) )
e + + ;
text . erase ( i , e ) ;
text . insert ( i , " " ) ;
}
parent_sequence - > Calls . back ( ) . Objects . push_back ( Object ( text ) ) ;
skip_ws ( nvl ) ;
} while ( nvl . tellg ( ) < end - static_cast < std : : streampos > ( 1 ) ) ;
nvl . get ( c ) ; // get >
skip_ws ( nvl ) ;
return ;
}
2021-05-14 23:17:55 -04:00
template < bool is_parent_call , bool is_symbol >
2021-05-14 20:44:09 -04:00
void parse_Object ( Context parent_context , std : : ifstream & nvl )
2021-05-14 16:51:04 -04:00
{
2021-05-19 17:02:57 -04:00
// chevron dialogue is now handled in a separate function
2021-05-14 20:44:09 -04:00
Object this_object ;
Context this_context = parent_context ;
2021-05-17 15:16:02 -04:00
this_context . Scope_Hierarchy . push_back ( & this_object ) ;
2021-05-14 23:17:55 -04:00
2021-05-15 17:48:37 -04:00
std : : variant < std : : string , std : : vector < Object > > content = " " ; // init to empty str
2021-05-15 04:12:26 -04:00
char c { } ;
2021-05-15 17:48:37 -04:00
// early exit for comments
2021-05-15 04:12:26 -04:00
if ( nvl . peek ( ) = = ' ; ' & & is_parent_call )
{
while ( nvl . peek ( ) ! = ' \n ' )
nvl . get ( c ) ;
return ;
}
2021-05-15 17:48:37 -04:00
2021-05-14 23:17:55 -04:00
switch ( nvl . peek ( ) )
2021-05-14 16:51:04 -04:00
{
2021-05-15 01:51:02 -04:00
case ' [ ' : // List
2021-05-19 17:02:57 -04:00
{
2021-05-15 17:48:37 -04:00
nvl . get ( c ) ;
2021-05-19 17:02:57 -04:00
std : : streampos end = scope_cope ( ' [ ' , nvl ) ;
2021-05-14 23:17:55 -04:00
while ( nvl . tellg ( ) < end - static_cast < std : : streampos > ( 1 ) )
2021-05-14 20:44:09 -04:00
{
parse_Object < false , false > ( this_context , nvl ) ;
2021-05-15 17:48:37 -04:00
skip_ws ( nvl ) ;
2021-05-14 20:44:09 -04:00
}
2021-05-15 17:48:37 -04:00
nvl . get ( c ) ; // skip ending scoper (']')
2021-05-14 20:44:09 -04:00
break ;
2021-05-19 17:02:57 -04:00
}
2021-05-14 23:17:55 -04:00
case ' \" ' : // String
2021-05-19 17:02:57 -04:00
{
2021-05-14 23:17:55 -04:00
nvl . get ( c ) ;
2021-05-19 17:02:57 -04:00
std : : streampos end = scope_cope ( ' \" ' , nvl ) ;
2021-05-14 23:17:55 -04:00
while ( nvl . tellg ( ) < end - static_cast < std : : streampos > ( 1 ) )
2021-05-14 20:44:09 -04:00
{
nvl . get ( c ) ;
2021-05-15 17:48:37 -04:00
// do not concat escaping '\' to content
2021-05-19 17:02:57 -04:00
if ( c = = ' \\ ' & & ( nvl . peek ( ) = = ' \' ' | | nvl . peek ( ) = = ' \" ' ) )
2021-05-15 01:51:02 -04:00
continue ;
2021-05-15 17:48:37 -04:00
2021-05-14 20:44:09 -04:00
content = std : : get < std : : string > ( content ) + c ;
}
2021-05-15 17:48:37 -04:00
nvl . get ( c ) ; // skip ending scoper
2021-05-14 23:17:55 -04:00
2021-05-14 20:44:09 -04:00
this_object . Value = std : : get < std : : string > ( content ) ;
break ;
2021-05-19 17:02:57 -04:00
}
2021-05-14 20:44:09 -04:00
default :
2021-05-19 17:02:57 -04:00
{
2021-05-15 17:48:37 -04:00
nvl . get ( c ) ;
while ( c ! = ' ' & & c ! = ' \n ' & & c ! = ' } ' & & c ! = ' ] ' & & c ! = ' , ' )
2021-05-14 20:44:09 -04:00
{
2021-05-19 17:02:57 -04:00
content = std : : get < std : : string > ( content ) + c ;
2021-05-15 17:48:37 -04:00
nvl . get ( c ) ;
2021-05-14 20:44:09 -04:00
}
2021-05-15 17:48:37 -04:00
// ']' handled in next object parse, '}' handled in sequence parse, '\n' will be skipped somewhere with skip_ws(), where? idk
2021-05-19 17:02:57 -04:00
if ( c = = ' ] ' | | c = = ' } ' | | c = = ' \n ' )
2021-05-15 04:12:26 -04:00
{
2021-05-14 20:44:09 -04:00
nvl . putback ( c ) ;
2021-05-15 04:12:26 -04:00
}
2021-05-14 23:17:55 -04:00
try
{
2021-05-15 17:48:37 -04:00
// try number
2021-05-14 23:17:55 -04:00
this_object . Value = std : : stof ( std : : get < std : : string > ( content ) ) ;
}
catch ( std : : exception )
{
2021-05-19 17:02:57 -04:00
if ( std : : get < std : : string > ( content ) = = " true " )
2021-05-14 23:17:55 -04:00
{
this_object . Value = true ;
}
else if ( std : : get < std : : string > ( content ) = = " false " ) {
this_object . Value = false ;
}
else
{
2021-05-15 17:48:37 -04:00
// early return if string is empty, this should only happen if calling from an object (not a call)
2021-05-19 17:02:57 -04:00
if ( str_test_every_char_not ( & isspace , std : : get < std : : string > ( content ) ) )
2021-05-16 12:36:44 -04:00
return ;
2021-05-15 17:48:37 -04:00
// default case if content does not match keywords
2021-05-14 23:17:55 -04:00
this_object . Value = std : : get < std : : string > ( content ) ;
}
}
2021-05-14 20:44:09 -04:00
break ;
2021-05-14 23:17:55 -04:00
}
2021-05-19 17:02:57 -04:00
}
2021-05-14 16:51:04 -04:00
2021-05-14 23:17:55 -04:00
this_object . Is_Symbol = is_symbol ;
2021-05-14 20:44:09 -04:00
if ( is_parent_call )
2021-05-17 15:16:02 -04:00
std : : get < Call * > ( parent_context . Scope_Hierarchy . back ( ) ) - > Objects . push_back ( this_object ) ;
else if ( std : : get < Object * > ( parent_context . Scope_Hierarchy . back ( ) ) - > Value . index ( ) = = 4 ) // 4 for list, indicates that parent is an object that is already a vector of objects
std : : get < std : : vector < Object > > (
std : : get < Object * > ( parent_context . Scope_Hierarchy . back ( ) ) - > Value
) . push_back ( this_object ) ;
2021-05-15 17:48:37 -04:00
else // parent is not yet vector, initialize as (change from nil to) vector
2021-05-17 15:16:02 -04:00
std : : get < Object * > ( parent_context . Scope_Hierarchy . back ( ) ) - > Value = std : : vector < Object > { this_object } ;
2021-05-19 17:02:57 -04:00
// the case for chevron dialogues are handled in the switch, there is an early return in the chevron case
2021-05-14 20:44:09 -04:00
}
void parse_Call ( Context parent_context , std : : ifstream & nvl )
{
Call this_call ;
Context this_context = parent_context ;
2021-05-17 15:16:02 -04:00
this_context . Scope_Hierarchy . push_back ( & this_call ) ;
2021-05-14 23:17:55 -04:00
2021-05-19 17:02:57 -04:00
skip_ws ( nvl ) ;
// do not push anything if line starts with a comment
char c { } ;
skip_comment ( nvl , c ) ;
// early exit if sequence is empty
if ( nvl . peek ( ) = = ' } ' )
return ;
2021-05-14 20:44:09 -04:00
2021-05-15 04:12:26 -04:00
while ( ! ( nvl . peek ( ) = = ' \n ' | | nvl . peek ( ) = = ' } ' ) )
2021-05-14 20:44:09 -04:00
{
2021-05-19 17:02:57 -04:00
skip_ws ( nvl ) ;
if ( nvl . peek ( ) = = ' < ' ) // dialogue! abandon current call, parse_Dialogue deals with pushing calls
{
parse_Dialogue ( this_context , nvl ) ;
return ;
}
2021-05-14 20:44:09 -04:00
parse_Object < true , false > ( this_context , nvl ) ;
}
2021-05-17 15:16:02 -04:00
std : : get < Sequence * > ( parent_context . Scope_Hierarchy . back ( ) ) - > Calls . push_back ( this_call ) ;
2021-05-14 11:47:54 -04:00
}
2021-05-14 16:51:04 -04:00
void parse_Sequence ( Context parent_context , std : : ifstream & nvl )
2021-05-14 11:47:54 -04:00
{
2021-05-15 13:38:10 -04:00
Sequence this_sequence ;
2021-05-14 16:51:04 -04:00
Context this_context = parent_context ;
2021-05-17 15:16:02 -04:00
this_context . Scope_Hierarchy . push_back ( & this_sequence ) ;
2021-05-14 11:47:54 -04:00
2021-05-15 17:48:37 -04:00
skip_ws ( nvl ) ;
2021-05-14 16:51:04 -04:00
2021-05-15 13:38:10 -04:00
char c { } ;
2021-05-19 17:02:57 -04:00
skip_comment ( nvl , c ) ;
2021-05-15 13:38:10 -04:00
this_sequence = Sequence ( read_sequence_name ( nvl ) ) ;
2021-05-15 17:48:37 -04:00
skip_ws ( nvl ) ;
2021-05-14 20:44:09 -04:00
nvl . get ( c ) ; // get {
2021-05-15 13:38:10 -04:00
if ( c ! = ' { ' )
2021-05-19 17:02:57 -04:00
throw std : : runtime_error ( " Sequence parse failed at line " + std : : to_string ( query_ifstream_line_number ( nvl ) ) ) ;
2021-05-14 16:51:04 -04:00
std : : streampos end_pos = scope_cope ( c , nvl ) ;
2021-05-14 20:44:09 -04:00
while ( nvl . tellg ( ) < end_pos - static_cast < std : : streampos > ( 1 ) )
2021-05-14 16:51:04 -04:00
{
2021-05-14 20:44:09 -04:00
parse_Call ( this_context , nvl ) ;
2021-05-15 17:48:37 -04:00
skip_ws ( nvl ) ;
2021-05-14 16:51:04 -04:00
}
2021-05-14 20:44:09 -04:00
nvl . get ( c ) ; // get }
2021-05-17 15:16:02 -04:00
std : : get < Tree * > ( parent_context . Scope_Hierarchy . back ( ) ) - > Sequences . push_back ( this_sequence ) ;
2021-05-14 11:47:54 -04:00
}
2021-05-14 16:51:04 -04:00
void parse_NVL ( Tree & root , std : : string path )
2021-05-14 11:47:54 -04:00
{
std : : ifstream nvl ;
2021-05-19 17:02:57 -04:00
std : : cout < < " Reading file " < < path < < " ... " < < std : : endl ;
2021-05-14 11:47:54 -04:00
nvl . open ( path ) ;
if ( nvl . is_open ( ) ) {
2021-05-14 16:51:04 -04:00
Context current_context ;
2021-05-17 15:16:02 -04:00
current_context . Scope_Hierarchy . push_back ( & root ) ;
2021-05-19 17:02:57 -04:00
skip_ws ( nvl ) ; // just in case the file is completely empty
2021-05-14 17:21:10 -04:00
while ( ! nvl . eof ( ) ) {
2021-05-15 17:48:37 -04:00
// parse_Sequence() already takes care of comments before a sequence
2021-05-14 16:51:04 -04:00
parse_Sequence ( current_context , nvl ) ;
2021-05-15 17:48:37 -04:00
skip_ws ( nvl ) ;
char c { } ;
2021-05-19 17:02:57 -04:00
skip_comment ( nvl , c ) ;
2021-05-15 17:48:37 -04:00
skip_ws ( nvl ) ;
2021-05-14 17:21:10 -04:00
}
2021-05-14 11:47:54 -04:00
nvl . close ( ) ;
2021-05-19 17:02:57 -04:00
} else
throw std : : runtime_error ( " Unable to read file " + path ) ;
2021-05-14 16:51:04 -04:00
}
2021-05-14 20:44:09 -04:00
2021-05-14 16:51:04 -04:00
void Object : : Print ( int indent )
{
indent_loop ( indent ) ;
std : : cout < < " Object: " ;
2021-05-15 17:48:37 -04:00
2021-05-14 16:51:04 -04:00
switch ( Value . index ( ) )
{
case 0 :
2021-05-14 23:17:55 -04:00
std : : cout < < " Nil " < < std : : endl ;
2021-05-14 16:51:04 -04:00
break ;
2021-05-14 20:44:09 -04:00
case 1 :
2021-05-19 17:02:57 -04:00
std : : cout < < " Float: " < < std : : get < float > ( Value ) < < std : : endl ;
2021-05-14 16:51:04 -04:00
break ;
2021-05-14 20:44:09 -04:00
case 2 :
2021-05-14 23:17:55 -04:00
if ( Is_Symbol )
2021-05-19 17:02:57 -04:00
std : : cout < < " Symbol: " < < std : : get < std : : string > ( Value ) < < std : : endl ;
2021-05-14 23:17:55 -04:00
else
2021-05-19 17:02:57 -04:00
std : : cout < < " String: " < < std : : get < std : : string > ( Value ) < < std : : endl ;
2021-05-14 16:51:04 -04:00
break ;
2021-05-14 20:44:09 -04:00
case 3 :
2021-05-19 17:02:57 -04:00
std : : cout < < " Bool: " < < std : : boolalpha < < std : : get < bool > ( Value ) < < std : : endl ;
2021-05-14 23:17:55 -04:00
break ;
case 4 :
2021-05-14 16:51:04 -04:00
std : : cout < < " List: " < < std : : endl ;
2021-05-15 17:48:37 -04:00
2021-05-14 16:51:04 -04:00
for ( auto & x : std : : get < std : : vector < Object > > ( Value ) )
{
x . Print ( indent + 1 ) ;
2021-05-14 23:17:55 -04:00
}
break ;
2021-05-14 16:51:04 -04:00
}
}
2021-05-15 17:48:37 -04:00
2021-05-14 16:51:04 -04:00
void Call : : Print ( int indent )
{
indent_loop ( indent ) ;
std : : cout < < " Call: " < < std : : endl ;
2021-05-15 17:48:37 -04:00
2021-05-14 16:51:04 -04:00
for ( auto & x : Objects )
{
x . Print ( indent + 1 ) ;
}
}
2021-05-15 17:48:37 -04:00
2021-05-14 16:51:04 -04:00
void Sequence : : Print ( int indent )
{
indent_loop ( indent ) ;
std : : cout < < " Sequence " < < Name < < " : " < < std : : endl ;
2021-05-15 17:48:37 -04:00
2021-05-14 16:51:04 -04:00
for ( auto & x : Calls )
{
x . Print ( indent + 1 ) ;
}
2021-05-15 17:48:37 -04:00
2021-05-14 20:44:09 -04:00
indent_loop ( indent ) ;
2021-05-15 17:48:37 -04:00
std : : cout < < " ----------------------------------------------- " < < std : : endl ;
2021-05-14 16:51:04 -04:00
}
2021-05-15 17:48:37 -04:00
2021-05-14 16:51:04 -04:00
void Tree : : Print ( int indent )
{
indent_loop ( indent ) ;
std : : cout < < " Tree: " < < std : : endl ;
2021-05-15 17:48:37 -04:00
2021-05-14 16:51:04 -04:00
for ( auto & x : Sequences )
{
x . Print ( indent + 1 ) ;
2021-05-14 11:47:54 -04:00
}
}
2021-05-14 17:21:10 -04:00
}