Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/Parser/parser.yy

    r7991c7d rdbedd71  
    1010// Created On       : Sat Sep  1 20:22:55 2001
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Fri Jul  1 15:35:08 2022
    13 // Update Count     : 5405
     12// Last Modified On : Fri Aug 12 07:59:58 2022
     13// Update Count     : 5649
    1414//
    1515
     
    5858
    5959// lex uses __null in a boolean context, it's fine.
    60 #pragma GCC diagnostic ignored "-Wparentheses-equality"
     60//#pragma GCC diagnostic ignored "-Wparentheses-equality"
    6161
    6262extern DeclarationNode * parseTree;
     
    197197} // fieldDecl
    198198
     199#define NEW_ZERO new ExpressionNode( build_constantInteger( *new string( "0" ) ) )
     200#define NEW_ONE  new ExpressionNode( build_constantInteger( *new string( "1" ) ) )
     201#define UPDOWN( compop, left, right ) (compop == OperKinds::LThan || compop == OperKinds::LEThan ? left : right)
     202
     203ForCtrl * forCtrl( DeclarationNode * index, ExpressionNode * start, enum OperKinds compop, ExpressionNode * comp, ExpressionNode * inc ) {
     204        if ( index->initializer ) {
     205                SemanticError( yylloc, "Direct initialization disallowed. Use instead: type var; initialization ~ comparison ~ increment." );
     206        } // if
     207        if ( index->next ) {
     208                SemanticError( yylloc, "Multiple loop indexes disallowed in for-loop declaration." );
     209        } // if
     210        return new ForCtrl( index->addInitializer( new InitializerNode( start ) ),
     211                // NULL comp/inc => leave blank
     212                comp ? new ExpressionNode( build_binary_val( compop, new ExpressionNode( build_varref( new string( *index->name ) ) ), comp ) ) : nullptr,
     213                inc ? new ExpressionNode( build_binary_val( compop == OperKinds::LThan || compop == OperKinds::LEThan ? // choose += or -= for upto/downto
     214                                                        OperKinds::PlusAssn : OperKinds::MinusAssn, new ExpressionNode( build_varref( new string( *index->name ) ) ), inc ) ) : nullptr );
     215} // forCtrl
     216
    199217ForCtrl * forCtrl( ExpressionNode * type, string * index, ExpressionNode * start, enum OperKinds compop, ExpressionNode * comp, ExpressionNode * inc ) {
    200218        ConstantExpr * constant = dynamic_cast<ConstantExpr *>(type->expr.get());
     
    206224                distAttr( DeclarationNode::newTypeof( type, true ), DeclarationNode::newName( index )->addInitializer( new InitializerNode( start ) ) ),
    207225                // NULL comp/inc => leave blank
    208                 comp ? new ExpressionNode( build_binary_val( compop, new ExpressionNode( build_varref( new string( *index ) ) ), comp ) ) : 0,
     226                comp ? new ExpressionNode( build_binary_val( compop, new ExpressionNode( build_varref( new string( *index ) ) ), comp ) ) : nullptr,
    209227                inc ? new ExpressionNode( build_binary_val( compop == OperKinds::LThan || compop == OperKinds::LEThan ? // choose += or -= for upto/downto
    210                                                         OperKinds::PlusAssn : OperKinds::MinusAssn, new ExpressionNode( build_varref( new string( *index ) ) ), inc ) ) : 0 );
     228                                                        OperKinds::PlusAssn : OperKinds::MinusAssn, new ExpressionNode( build_varref( new string( *index ) ) ), inc ) ) : nullptr );
    211229} // forCtrl
    212230
     
    346364%type<ifctl> conditional_declaration
    347365%type<fctl> for_control_expression              for_control_expression_list
    348 %type<compop> inclexcl
     366%type<compop> updown updowneq downupdowneq
    349367%type<en> subrange
    350368%type<decl> asm_name_opt
     
    12391257iteration_statement:
    12401258        WHILE '(' ')' statement                                                         %prec THEN // CFA => while ( 1 )
    1241                 { $$ = new StatementNode( build_while( new CondCtl( nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ), maybe_build_compound( $4 ) ) ); }
     1259                { $$ = new StatementNode( build_while( new CondCtl( nullptr, NEW_ONE ), maybe_build_compound( $4 ) ) ); }
    12421260        | WHILE '(' ')' statement ELSE statement                        // CFA
    12431261                {
    1244                         $$ = new StatementNode( build_while( new CondCtl( nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ), maybe_build_compound( $4 ) ) );
     1262                        $$ = new StatementNode( build_while( new CondCtl( nullptr, NEW_ONE ), maybe_build_compound( $4 ) ) );
    12451263                        SemanticWarning( yylloc, Warning::SuperfluousElse, "" );
    12461264                }
     
    12501268                { $$ = new StatementNode( build_while( $3, maybe_build_compound( $5 ), $7 ) ); }
    12511269        | DO statement WHILE '(' ')' ';'                                        // CFA => do while( 1 )
    1252                 { $$ = new StatementNode( build_do_while( new ExpressionNode( build_constantInteger( *new string( "1" ) ) ), maybe_build_compound( $2 ) ) ); }
     1270                { $$ = new StatementNode( build_do_while( NEW_ONE, maybe_build_compound( $2 ) ) ); }
    12531271        | DO statement WHILE '(' ')' ELSE statement                     // CFA
    12541272                {
    1255                         $$ = new StatementNode( build_do_while( new ExpressionNode( build_constantInteger( *new string( "1" ) ) ), maybe_build_compound( $2 ) ) );
     1273                        $$ = new StatementNode( build_do_while( NEW_ONE, maybe_build_compound( $2 ) ) );
    12561274                        SemanticWarning( yylloc, Warning::SuperfluousElse, "" );
    12571275                }
     
    13051323
    13061324        | comma_expression                                                                      // CFA
    1307                 { $$ = forCtrl( $1, new string( DeclarationNode::anonymous.newName() ), new ExpressionNode( build_constantInteger( *new string( "0" ) ) ),
    1308                                                 OperKinds::LThan, $1->clone(), new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); }
    1309         | '=' comma_expression                                                          // CFA
    1310                 { $$ = forCtrl( $2, new string( DeclarationNode::anonymous.newName() ), new ExpressionNode( build_constantInteger( *new string( "0" ) ) ),
    1311                                                 OperKinds::LEThan, $2->clone(), new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); }
    1312         | comma_expression inclexcl comma_expression            // CFA
    1313                 { $$ = forCtrl( $1, new string( DeclarationNode::anonymous.newName() ), $1->clone(), $2, $3, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); }
    1314         | comma_expression inclexcl comma_expression '~' comma_expression // CFA
    1315                 { $$ = forCtrl( $1, new string( DeclarationNode::anonymous.newName() ), $1->clone(), $2, $3, $5 ); }
    1316         | comma_expression ';'                                                          // CFA
    1317                 { $$ = forCtrl( new ExpressionNode( build_constantInteger( *new string( "0u" ) ) ), $1, nullptr, OperKinds::LThan, nullptr, nullptr ); }
     1325                { $$ = forCtrl( $1, new string( DeclarationNode::anonymous.newName() ), NEW_ZERO, OperKinds::LThan, $1->clone(), NEW_ONE ); }
     1326        | downupdowneq comma_expression                                         // CFA
     1327                { $$ = forCtrl( $2, new string( DeclarationNode::anonymous.newName() ), UPDOWN( $1, NEW_ZERO, $2->clone() ), $1, UPDOWN( $1, $2->clone(), NEW_ZERO ), NEW_ONE ); }
     1328
     1329        | comma_expression updowneq comma_expression            // CFA
     1330                { $$ = forCtrl( $1, new string( DeclarationNode::anonymous.newName() ), UPDOWN( $2, $1->clone(), $3 ), $2, UPDOWN( $2, $3->clone(), $1->clone() ), NEW_ONE ); }
     1331        | '@' updowneq comma_expression                                         // CFA
     1332                {
     1333                        if ( $2 == OperKinds::LThan || $2 == OperKinds::LEThan ) { SemanticError( yylloc, "Missing start value so cannot compare." ); $$ = nullptr; }
     1334                        else $$ = forCtrl( $3, new string( DeclarationNode::anonymous.newName() ), $3->clone(), $2, nullptr, NEW_ONE );
     1335                }
     1336        | comma_expression updowneq '@'                                         // CFA
     1337                {
     1338                        if ( $2 == OperKinds::LThan || $2 == OperKinds::LEThan ) { SemanticError( yylloc, "Missing comparison ('@') with an anonymous loop index is meaningless." ); $$ = nullptr; }
     1339                        else { SemanticError( yylloc, "Missing start value so cannot compare." ); $$ = nullptr; }
     1340                }
     1341        | comma_expression updowneq comma_expression '~' comma_expression // CFA
     1342                { $$ = forCtrl( $1, new string( DeclarationNode::anonymous.newName() ), UPDOWN( $2, $1->clone(), $3 ), $2, UPDOWN( $2, $3->clone(), $1->clone() ), $5 ); }
     1343        | '@' updowneq comma_expression '~' comma_expression // CFA
     1344                {
     1345                        if ( $2 == OperKinds::LThan || $2 == OperKinds::LEThan ) { SemanticError( yylloc, "Missing start value so cannot compare." ); $$ = nullptr; }
     1346                        else $$ = forCtrl( $3, new string( DeclarationNode::anonymous.newName() ), $3->clone(), $2, nullptr, $5 );
     1347                }
     1348        | comma_expression updowneq '@' '~' comma_expression // CFA
     1349                {
     1350                        if ( $2 == OperKinds::LThan || $2 == OperKinds::LEThan ) { SemanticError( yylloc, "Missing comparison ('@') with an anonymous loop index is meaningless." ); $$ = nullptr; }
     1351                        else { SemanticError( yylloc, "Missing start value so cannot compare." ); $$ = nullptr; }
     1352                }
     1353        | comma_expression updowneq comma_expression '~' '@' // CFA, error
     1354                { SemanticError( yylloc, "Missing increment ('@') with an anonymous loop index is meaningless." ); $$ = nullptr; }
     1355        | '@' updowneq comma_expression '~' '@'                         // CFA, error
     1356                { SemanticError( yylloc, "Missing loop fields ('@') with an anonymous loop index is meaningless." ); $$ = nullptr; }
     1357        | comma_expression updowneq '@' '~' '@'                         // CFA, error
     1358                { SemanticError( yylloc, "Missing loop fields ('@') with an anonymous loop index is meaningless." ); $$ = nullptr; }
     1359        | '@' updowneq '@' '~' '@'                                                      // CFA, error
     1360                { SemanticError( yylloc, "Missing loop fields ('@') with an anonymous loop index is meaningless." ); $$ = nullptr; }
     1361
    13181362        | comma_expression ';' comma_expression                         // CFA
    1319                 { $$ = forCtrl( $3, $1, new ExpressionNode( build_constantInteger( *new string( "0" ) ) ),
    1320                                                 OperKinds::LThan, $3->clone(), new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); }
    1321         | comma_expression ';' '=' comma_expression                     // CFA
    1322                 { $$ = forCtrl( $4, $1, new ExpressionNode( build_constantInteger( *new string( "0" ) ) ),
    1323                                                 OperKinds::LEThan, $4->clone(), new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); }
    1324         | comma_expression ';' comma_expression inclexcl comma_expression // CFA
    1325                 { $$ = forCtrl( $3, $1, $3->clone(), $4, $5, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); }
    1326         | comma_expression ';' comma_expression inclexcl comma_expression '~' comma_expression // CFA
    1327                 { $$ = forCtrl( $3, $1, $3->clone(), $4, $5, $7 ); }
     1363                { $$ = forCtrl( $3, $1, NEW_ZERO, OperKinds::LThan, $3->clone(), NEW_ONE ); }
     1364        | comma_expression ';' downupdowneq comma_expression // CFA
     1365                { $$ = forCtrl( $4, $1, UPDOWN( $3, NEW_ZERO, $4->clone() ), $3, UPDOWN( $3, $4->clone(), NEW_ZERO ), NEW_ONE ); }
     1366
     1367        | comma_expression ';' comma_expression updowneq comma_expression // CFA
     1368                { $$ = forCtrl( $3, $1, UPDOWN( $4, $3->clone(), $5 ), $4, UPDOWN( $4, $5->clone(), $3->clone() ), NEW_ONE ); }
     1369        | comma_expression ';' '@' updowneq comma_expression // CFA
     1370                {
     1371                        if ( $4 == OperKinds::LThan || $4 == OperKinds::LEThan ) { SemanticError( yylloc, "Missing start value so cannot compare." ); $$ = nullptr; }
     1372                        else $$ = forCtrl( $5, $1, $5->clone(), $4, nullptr, NEW_ONE );
     1373                }
     1374        | comma_expression ';' comma_expression updowneq '@' // CFA
     1375                {
     1376                        if ( $4 == OperKinds::GThan || $4 == OperKinds::GEThan ) { SemanticError( yylloc, "Missing start value so cannot compare." ); $$ = nullptr; }
     1377                        else if ( $4 == OperKinds::LEThan ) { SemanticError( yylloc, "Equality with missing comparison is meaningless. Use \"~\"." ); $$ = nullptr; }
     1378                        else $$ = forCtrl( $3, $1, $3->clone(), $4, nullptr, NEW_ONE );
     1379                }
     1380        | comma_expression ';' '@' updowneq '@'                         // CFA, error
     1381                { SemanticError( yylloc, "Missing start value so cannot compare." ); $$ = nullptr; }
     1382
     1383        | comma_expression ';' comma_expression updowneq comma_expression '~' comma_expression // CFA
     1384                { $$ = forCtrl( $3, $1, UPDOWN( $4, $3->clone(), $5 ), $4, UPDOWN( $4, $5->clone(), $3->clone() ), $7 ); }
     1385        | comma_expression ';' '@' updowneq comma_expression '~' comma_expression // CFA, error
     1386                {
     1387                        if ( $4 == OperKinds::LThan || $4 == OperKinds::LEThan ) { SemanticError( yylloc, "Missing start value so cannot compare." ); $$ = nullptr; }
     1388                        else $$ = forCtrl( $5, $1, $5->clone(), $4, nullptr, $7 );
     1389                }
     1390        | comma_expression ';' comma_expression updowneq '@' '~' comma_expression // CFA
     1391                {
     1392                        if ( $4 == OperKinds::GThan || $4 == OperKinds::GEThan ) { SemanticError( yylloc, "Missing start value so cannot compare." ); $$ = nullptr; }
     1393                        else if ( $4 == OperKinds::LEThan ) { SemanticError( yylloc, "Equality with missing comparison is meaningless. Use \"~\"." ); $$ = nullptr; }
     1394                        else $$ = forCtrl( $3, $1, $3->clone(), $4, nullptr, $7 );
     1395                }
     1396        | comma_expression ';' comma_expression updowneq comma_expression '~' '@' // CFA
     1397                { $$ = forCtrl( $3, $1, UPDOWN( $4, $3->clone(), $5 ), $4, UPDOWN( $4, $5->clone(), $3->clone() ), nullptr ); }
     1398        | comma_expression ';' '@' updowneq comma_expression '~' '@' // CFA, error
     1399                {
     1400                        if ( $4 == OperKinds::LThan || $4 == OperKinds::LEThan ) { SemanticError( yylloc, "Missing start value so cannot compare." ); $$ = nullptr; }
     1401                        else $$ = forCtrl( $5, $1, $5->clone(), $4, nullptr, nullptr );
     1402                }
     1403        | comma_expression ';' comma_expression updowneq '@' '~' '@' // CFA
     1404                {
     1405                        if ( $4 == OperKinds::GThan || $4 == OperKinds::GEThan ) { SemanticError( yylloc, "Missing start value so cannot compare." ); $$ = nullptr; }
     1406                        else if ( $4 == OperKinds::LEThan ) { SemanticError( yylloc, "Equality with missing comparison is meaningless. Use \"~\"." ); $$ = nullptr; }
     1407                        else $$ = forCtrl( $3, $1, $3->clone(), $4, nullptr, nullptr );
     1408                }
     1409        | comma_expression ';' '@' updowneq '@' '~' '@' // CFA
     1410                { SemanticError( yylloc, "Missing start value so cannot compare." ); $$ = nullptr; }
     1411
     1412        | declaration comma_expression                                          // CFA
     1413                { $$ = forCtrl( $1, NEW_ZERO, OperKinds::LThan, $2, NEW_ONE ); }
     1414        | declaration downupdowneq comma_expression                     // CFA
     1415                { $$ = forCtrl( $1, UPDOWN( $2, NEW_ZERO, $3 ), $2, UPDOWN( $2, $3->clone(), NEW_ZERO ), NEW_ONE ); }
     1416
     1417        | declaration comma_expression updowneq comma_expression // CFA
     1418                { $$ = forCtrl( $1, UPDOWN( $3, $2->clone(), $4 ), $3, UPDOWN( $3, $4->clone(), $2->clone() ), NEW_ONE ); }
     1419        | declaration '@' updowneq comma_expression                     // CFA
     1420                {
     1421                        if ( $3 == OperKinds::LThan || $3 == OperKinds::LEThan ) { SemanticError( yylloc, "Missing start value so cannot compare." ); $$ = nullptr; }
     1422                        else $$ = forCtrl( $1, $4, $3, nullptr, NEW_ONE );
     1423                }
     1424        | declaration comma_expression updowneq '@'                     // CFA
     1425                {
     1426                        if ( $3 == OperKinds::GThan || $3 == OperKinds::GEThan ) { SemanticError( yylloc, "Missing start value so cannot compare." ); $$ = nullptr; }
     1427                        else if ( $3 == OperKinds::LEThan ) { SemanticError( yylloc, "Equality with missing comparison is meaningless. Use \"~\"." ); $$ = nullptr; }
     1428                        else $$ = forCtrl( $1, $2, $3, nullptr, NEW_ONE );
     1429                }
     1430
     1431        | declaration comma_expression updowneq comma_expression '~' comma_expression // CFA
     1432                { $$ = forCtrl( $1, UPDOWN( $3, $2, $4 ), $3, UPDOWN( $3, $4->clone(), $2->clone() ), $6 ); }
     1433        | declaration '@' updowneq comma_expression '~' comma_expression // CFA
     1434                {
     1435                        if ( $3 == OperKinds::LThan || $3 == OperKinds::LEThan ) { SemanticError( yylloc, "Missing start value so cannot compare." ); $$ = nullptr; }
     1436                        else $$ = forCtrl( $1, $4, $3, nullptr, $6 );
     1437                }
     1438        | declaration comma_expression updowneq '@' '~' comma_expression // CFA
     1439                {
     1440                        if ( $3 == OperKinds::GThan || $3 == OperKinds::GEThan ) { SemanticError( yylloc, "Missing start value so cannot compare." ); $$ = nullptr; }
     1441                        else if ( $3 == OperKinds::LEThan ) { SemanticError( yylloc, "Equality with missing comparison is meaningless. Use \"~\"." ); $$ = nullptr; }
     1442                        else $$ = forCtrl( $1, $2, $3, nullptr, $6 );
     1443                }
     1444        | declaration comma_expression updowneq comma_expression '~' '@' // CFA
     1445                { $$ = forCtrl( $1, UPDOWN( $3, $2, $4 ), $3, UPDOWN( $3, $4->clone(), $2->clone() ), nullptr ); }
     1446        | declaration '@' updowneq comma_expression '~' '@' // CFA
     1447                {
     1448                        if ( $3 == OperKinds::LThan || $3 == OperKinds::LEThan ) { SemanticError( yylloc, "Missing start value so cannot compare." ); $$ = nullptr; }
     1449                        else $$ = forCtrl( $1, $4, $3, nullptr, nullptr );
     1450                }
     1451        | declaration comma_expression updowneq '@' '~' '@'     // CFA
     1452                {
     1453                        if ( $3 == OperKinds::GThan || $3 == OperKinds::GEThan ) { SemanticError( yylloc, "Missing start value so cannot compare." ); $$ = nullptr; }
     1454                        else if ( $3 == OperKinds::LEThan ) { SemanticError( yylloc, "Equality with missing comparison is meaningless. Use \"~\"." ); $$ = nullptr; }
     1455                        else $$ = forCtrl( $1, $2, $3, nullptr, nullptr );
     1456                }
     1457        | declaration '@' updowneq '@' '~' '@'                          // CFA, error
     1458                { SemanticError( yylloc, "Missing start value so cannot compare." ); $$ = nullptr; }
    13281459
    13291460        | comma_expression ';' TYPEDEFname                                      // CFA, array type
    13301461                {
    1331                         SemanticError( yylloc, "Array interator is currently unimplemented." ); $$ = nullptr;
    1332                         $$ = forCtrl( new ExpressionNode( build_varref( $3 ) ), $1, nullptr, OperKinds::Range, nullptr, nullptr );
    1333                 }
    1334 
    1335                 // There is a S/R conflicit if ~ and -~ are factored out.
    1336         | comma_expression ';' comma_expression '~' '@'         // CFA
    1337                 { $$ = forCtrl( $3, $1, $3->clone(), OperKinds::LThan, nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); }
    1338         | comma_expression ';' comma_expression ErangeDown '@' // CFA
    1339                 { $$ = forCtrl( $3, $1, $3->clone(), OperKinds::GThan, nullptr, new ExpressionNode( build_constantInteger( *new string( "1" ) ) ) ); }
    1340         | comma_expression ';' comma_expression '~' '@' '~' comma_expression // CFA
    1341                 { $$ = forCtrl( $3, $1, $3->clone(), OperKinds::LThan, nullptr, $7 ); }
    1342         | comma_expression ';' comma_expression ErangeDown '@' '~' comma_expression // CFA
    1343                 { $$ = forCtrl( $3, $1, $3->clone(), OperKinds::GThan, nullptr, $7 ); }
    1344         | comma_expression ';' comma_expression '~' '@' '~' '@' // CFA
    1345                 { $$ = forCtrl( $3, $1, $3->clone(), OperKinds::LThan, nullptr, nullptr ); }
     1462                        SemanticError( yylloc, "Type iterator is currently unimplemented." ); $$ = nullptr;
     1463                        //$$ = forCtrl( new ExpressionNode( build_varref( $3 ) ), $1, nullptr, OperKinds::Range, nullptr, nullptr );
     1464                }
     1465        | comma_expression ';' downupdowneq TYPEDEFname         // CFA, array type
     1466                {
     1467                        if ( $3 == OperKinds::LEThan || $3 == OperKinds::GEThan ) { SemanticError( yylloc, "All enumation ranges are equal (all values). Remove \"=~\"." ); $$ = nullptr; }
     1468                        SemanticError( yylloc, "Type iterator is currently unimplemented." ); $$ = nullptr;
     1469                }
    13461470        ;
    13471471
    1348 inclexcl:
     1472downupdowneq:
     1473        ErangeDown
     1474                { $$ = OperKinds::GThan; }
     1475        | ErangeUpEq
     1476                { $$ = OperKinds::LEThan; }
     1477        | ErangeDownEq
     1478                { $$ = OperKinds::GEThan; }
     1479        ;
     1480
     1481updown:
    13491482        '~'
    13501483                { $$ = OperKinds::LThan; }
     1484        | ErangeDown
     1485                { $$ = OperKinds::GThan; }
     1486        ;
     1487
     1488updowneq:
     1489        updown
    13511490        | ErangeUpEq
    13521491                { $$ = OperKinds::LEThan; }
    1353         | ErangeDown
    1354                 { $$ = OperKinds::GThan; }
    13551492        | ErangeDownEq
    13561493                { $$ = OperKinds::GEThan; }
     
    23952532          '{' enumerator_list comma_opt '}'
    23962533                { $$ = DeclarationNode::newEnum( $3->name, $5, true )->addQualifiers( $2 ); }
     2534        | ENUM '(' ')' attribute_list_opt '{' enumerator_list comma_opt '}'
     2535                { SemanticError( yylloc, "Unvalued enumerated type is currently unimplemented." ); $$ = nullptr; }
    23972536        | ENUM '(' cfa_abstract_parameter_declaration ')' attribute_list_opt '{' enumerator_list comma_opt '}'
    23982537                {
Note: See TracChangeset for help on using the changeset viewer.