diff options
author | Adrian Thurston <thurston@complang.org> | 2012-01-08 01:46:51 +0000 |
---|---|---|
committer | Adrian Thurston <thurston@complang.org> | 2012-01-08 01:46:51 +0000 |
commit | 99d6b340c6092e614a20757cac64cb21576ac99e (patch) | |
tree | 05bde0e19a0724617ec0282a5a07d7f9d3b8e00c | |
parent | bb80df40fd589013ed343bed280317c8d963a87a (diff) | |
download | colm-99d6b340c6092e614a20757cac64cb21576ac99e.tar.gz |
Eliminated notoken. The reverse execution items are collected and attached to
the next object put into the parser. refs #336.
-rw-r--r-- | colm/bytecode.c | 42 | ||||
-rw-r--r-- | colm/codegen.cc | 5 | ||||
-rw-r--r-- | colm/colm.h | 2 | ||||
-rw-r--r-- | colm/pdarun.c | 80 | ||||
-rw-r--r-- | colm/pdarun.h | 3 | ||||
-rw-r--r-- | colm/program.c | 5 | ||||
-rw-r--r-- | colm/tree.c | 2 | ||||
-rw-r--r-- | test/TESTS | 4 | ||||
-rw-r--r-- | test/accumbt1.exp (renamed from test/accumbt.exp) | 0 | ||||
-rw-r--r-- | test/accumbt1.lm (renamed from test/accumbt.lm) | 0 | ||||
-rw-r--r-- | test/accumbt2.exp | 4 | ||||
-rw-r--r-- | test/accumbt3.exp | 9 | ||||
-rw-r--r-- | test/accumbt3.in | 1 | ||||
-rw-r--r-- | test/accumbt3.lm | 85 | ||||
-rw-r--r-- | test/nestedcomm.exp | 2 | ||||
-rwxr-xr-x | test/runtests.mk | 44 |
16 files changed, 206 insertions, 82 deletions
diff --git a/colm/bytecode.c b/colm/bytecode.c index dbff5b2a..62bd63b5 100644 --- a/colm/bytecode.c +++ b/colm/bytecode.c @@ -270,6 +270,11 @@ case PcrReverse: } } + /* FIXME: need something here to check that we aren' stopped waiting for + * more data when we are actually expected to finish. This check doesn't + * work (at time of writing). */ + //assert( (parser->pdaRun->stopTarget > 0 && parser->pdaRun->stopParsing) || parser->input->in->eofSent ); + if ( !revertOn ) commitFull( prg, sp, parser->pdaRun, 0 ); @@ -782,10 +787,14 @@ int makeReverseCode( PdaRun *pdaRun ) if ( rcodeCollect->tabLen == 0 ) return false; - /* One reverse code run for the DECK terminator. */ - append( reverseCode, IN_PCR_END_DECK ); - append( reverseCode, IN_PCR_RET ); - appendWord( reverseCode, 2 ); + if ( pdaRun->rcBlockCount == 0 ) { + /* One reverse code run for the DECK terminator. */ + append( reverseCode, IN_PCR_END_DECK ); + append( reverseCode, IN_PCR_RET ); + appendWord( reverseCode, 2 ); + pdaRun->rcBlockCount += 1; + incrementSteps( pdaRun ); + } long startLength = reverseCode->tabLen; @@ -807,9 +816,21 @@ int makeReverseCode( PdaRun *pdaRun ) /* Clear the revere code buffer. */ rcodeCollect->tabLen = 0; + pdaRun->rcBlockCount += 1; + incrementSteps( pdaRun ); + return true; } +void transferReverseCode( PdaRun *pdaRun, Tree *tree ) +{ + if ( pdaRun->rcBlockCount > 0 ) { + debug( REALM_PARSE, "attaching reverse code to token\n" ); + tree->flags |= AF_HAS_RCODE; + pdaRun->rcBlockCount = 0; + } +} + Code *popReverseCode( RtCodeVect *allRev ) { /* Read the length */ @@ -823,14 +844,6 @@ Code *popReverseCode( RtCodeVect *allRev ) /* Backup over it. */ allRev->tabLen -= len + SIZEOF_WORD; - -// /* Do it again for the terminator. */ -// Code *prcode2 = allRev->data + allRev->tabLen - SIZEOF_WORD; -// read_word_p( len, prcode2 ); -// start = allRev->tabLen - len - SIZEOF_WORD; -// prcode2 = allRev->data + start; -// allRev->tabLen -= len + SIZEOF_WORD; - return prcode; } @@ -2277,13 +2290,10 @@ again: case IN_PARSE_FRAG_WV3: { debug( REALM_BYTECODE, "IN_PARSE_FRAG_WV3 \n" ); - long pcr = (long)vm_pop(); + vm_pop_ignore(); Parser *parser = (Parser*)vm_pop(); long steps = (long)vm_pop(); - debug( REALM_BYTECODE, "pcr: %ld\n", pcr ); - debug( REALM_BYTECODE, "steps: %ld\n", steps ); - append( &exec->pdaRun->rcodeCollect, IN_LOAD_WORD ); appendWord( &exec->pdaRun->rcodeCollect, steps ); append( &exec->pdaRun->rcodeCollect, IN_LOAD_TREE ); diff --git a/colm/codegen.cc b/colm/codegen.cc index ef0072fb..b7ec85b3 100644 --- a/colm/codegen.cc +++ b/colm/codegen.cc @@ -201,11 +201,12 @@ void FsmCodeGen::writeMain() "int main( int argc, const char **argv )\n" "{\n" " struct ColmProgram *prg;\n" + " int exitStatus;\n" " colmInit( " << colmActiveRealm << " );\n" " prg = colmNewProgram( &main_runtimeData, argc, argv );\n" " colmRunProgram( prg );\n" - " colmDeleteProgram( prg );\n" - " return prg->exitStatus;\n" + " exitStatus = colmDeleteProgram( prg );\n" + " return exitStatus;\n" "}\n" "\n"; diff --git a/colm/colm.h b/colm/colm.h index d376f20f..0f021c6a 100644 --- a/colm/colm.h +++ b/colm/colm.h @@ -13,7 +13,7 @@ struct ColmRuntimeData; void colmInit( long debugRealm ); struct ColmProgram *colmNewProgram( struct ColmRuntimeData *rtd, int argc, const char **argv ); void colmRunProgram( struct ColmProgram *prg ); -void colmDeleteProgram( struct ColmProgram *prg ); +int colmDeleteProgram( struct ColmProgram *prg ); struct ColmPrintArgs { diff --git a/colm/pdarun.c b/colm/pdarun.c index a923203d..486fbd60 100644 --- a/colm/pdarun.c +++ b/colm/pdarun.c @@ -412,21 +412,30 @@ static void sendBack( Program *prg, Tree **sp, PdaRun *pdaRun, FsmRun *fsmRun, /* Artifical were not parsed, instead sent in as items. */ if ( input->tree->flags & AF_ARTIFICIAL ) { + /* Check for reverse code. */ + if ( input->tree->flags & AF_HAS_RCODE ) { + debug( REALM_PARSE, "tree has rcode, setting on deck\n" ); + pdaRun->onDeck = true; + input->tree->flags &= ~AF_HAS_RCODE; + } + treeUpref( input->tree ); streamPushTree( fsmRun, inputStream, input->tree, false ); } else { - /* Push back the token data. */ - sendBackText( fsmRun, inputStream, stringData( input->tree->tokdata ), - stringLength( input->tree->tokdata ) ); - /* Check for reverse code. */ if ( input->tree->flags & AF_HAS_RCODE ) { + debug( REALM_PARSE, "tree has rcode, setting on deck\n" ); pdaRun->onDeck = true; input->tree->flags &= ~AF_HAS_RCODE; } + /* Push back the token data. */ + sendBackText( fsmRun, inputStream, stringData( input->tree->tokdata ), + stringLength( input->tree->tokdata ) ); + + /* If eof was just sent back remember that it needs to be sent again. */ if ( input->tree->id == prg->rtd->eofLelIds[pdaRun->parserId] ) inputStream->eofSent = false; @@ -457,6 +466,10 @@ void setRegion( PdaRun *pdaRun, Tree *tree ) void ignoreTree( Program *prg, PdaRun *pdaRun, Tree *tree ) { + transferReverseCode( pdaRun, tree ); + + incrementSteps( pdaRun ); + setRegion( pdaRun, tree ); /* Add the ignore string to the head of the ignore list. */ @@ -510,37 +523,6 @@ Kid *makeTokenWithData( Program *prg, PdaRun *pdaRun, FsmRun *fsmRun, InputStrea return input; } -void addNoToken( Program *prg, Tree **sp, FsmRun *fsmRun, PdaRun *pdaRun, - InputStream *inputStream, int frameId, long id, Head *tokdata ) -{ - /* Check if there was anything generated. */ - if ( pdaRun->rcodeCollect.tabLen > 0 ) { - debug( REALM_PARSE, "found reverse code but no token, sending _notoken\n" ); - - Tree *tree = (Tree*)parseTreeAllocate( prg ); - tree->flags |= AF_PARSE_TREE; - tree->refs = 1; - tree->id = prg->rtd->noTokenId; - tree->tokdata = 0; - - Kid *send = kidAllocate( prg ); - send->tree = tree; - send->next = 0; - - /* If there is reverse code then addNoToken will guarantee that the - * queue is not empty. Pull the reverse code out and store in the - * token. */ - int hasrcode = makeReverseCode( pdaRun ); - if ( hasrcode ) - tree->flags |= AF_HAS_RCODE; - - incrementSteps( pdaRun ); - - ignoreTree( prg, pdaRun, send->tree ); - kidFree( prg, send ); - } -} - Kid *extractIgnore( PdaRun *pdaRun ) { Kid *ignore = pdaRun->accumIgnore; @@ -700,6 +682,7 @@ void handleError( Program *prg, Tree **sp, PdaRun *pdaRun ) } } + void sendIgnore( Program *prg, Tree **sp, InputStream *inputStream, FsmRun *fsmRun, PdaRun *pdaRun, long id ) { debug( REALM_PARSE, "ignoring: %s\n", prg->rtd->lelInfo[id].name ); @@ -716,8 +699,6 @@ void sendIgnore( Program *prg, Tree **sp, InputStream *inputStream, FsmRun *fsmR tree->id = id; tree->tokdata = ignoreStr; - incrementSteps( pdaRun ); - /* Send it to the pdaRun. */ ignoreTree( prg, pdaRun, tree ); } @@ -790,9 +771,6 @@ static Kid *sendTree( Program *prg, Tree **sp, PdaRun *pdaRun, FsmRun *fsmRun, I static void sendIgnoreTree( Program *prg, Tree **sp, PdaRun *pdaRun, FsmRun *fsmRun, InputStream *inputStream ) { Tree *tree = consumeTree( inputStream ); - - incrementSteps( pdaRun ); - ignoreTree( prg, pdaRun, tree ); } @@ -1075,11 +1053,12 @@ case PcrStart: return PcrPreEof; case PcrPreEof: + {} /* * Need a no-token. */ - addNoToken( prg, sp, fsmRun, pdaRun, inputStream, pdaRun->frameId, pdaRun->tokenId, 0 ); + //addNoToken( prg, sp, fsmRun, pdaRun, inputStream, pdaRun->frameId, pdaRun->tokenId, 0 ); } } else if ( pdaRun->tokenId == SCAN_UNDO ) { @@ -1156,8 +1135,10 @@ case PcrGeneration: /* * May need a no-token. */ - addNoToken( prg, sp, fsmRun, pdaRun, inputStream, - prg->rtd->lelInfo[pdaRun->tokenId].frameId, pdaRun->tokenId, pdaRun->tokdata ); + //addNoToken( prg, sp, fsmRun, pdaRun, inputStream, + // prg->rtd->lelInfo[pdaRun->tokenId].frameId, pdaRun->tokenId, pdaRun->tokdata ); + + makeReverseCode( pdaRun ); /* Finished with the match text. */ stringFree( prg, pdaRun->tokdata ); @@ -1180,6 +1161,9 @@ case PcrGeneration: pdaRun->input1 = sendToken( prg, sp, inputStream, fsmRun, pdaRun, pdaRun->tokenId ); } + if ( pdaRun->input1 != 0 ) + transferReverseCode( pdaRun, pdaRun->input1->tree ); + long pcr = parseToken( prg, sp, pdaRun, fsmRun, inputStream, PcrStart ); while ( pcr != PcrDone ) { @@ -1356,6 +1340,8 @@ void initPdaRun( PdaRun *pdaRun, Program *prg, PdaTables *tables, pdaRun->onDeck = false; pdaRun->parsed = 0; pdaRun->reject = false; + + pdaRun->rcBlockCount = 0; } long stackTopTarget( Program *prg, PdaRun *pdaRun ) @@ -1810,9 +1796,8 @@ case PcrReduction: } /* Pull out the reverse code, if any. */ - int hasrcode = makeReverseCode( pdaRun ); - if ( hasrcode ) - pdaRun->redLel->tree->flags |= AF_HAS_RCODE; + makeReverseCode( pdaRun ); + transferReverseCode( pdaRun, pdaRun->redLel->tree ); /* Perhaps the execution environment is telling us we need to * reject the reduction. */ @@ -1859,6 +1844,7 @@ parseError: return PcrReverse; case PcrReverse: + decrementSteps( pdaRun ); {} } @@ -1937,6 +1923,7 @@ case PcrReverse: } } else if ( pdaRun->input1->tree->flags & AF_HAS_RCODE ) { + debug( REALM_PARSE, "tree has rcode, setting on deck\n" ); pdaRun->onDeck = true; pdaRun->parsed = 0; @@ -1950,7 +1937,6 @@ case PcrReverse: pdaRun->undoLel = pdaRun->input1; pdaRun->input1 = pdaRun->input1->next; - /* Extract the real children from the child list. */ Kid *first = treeExtractChild( prg, pdaRun->undoLel->tree ); diff --git a/colm/pdarun.h b/colm/pdarun.h index 6dc3ff27..11a4bfa3 100644 --- a/colm/pdarun.h +++ b/colm/pdarun.h @@ -337,6 +337,8 @@ typedef struct _PdaRun /* Instruction pointer to use when we stop parsing and execute code. */ Code *code; + + int rcBlockCount; } PdaRun; void rtCodeVectReplace( RtCodeVect *vect, long pos, const Code *val, long len ); @@ -387,6 +389,7 @@ void incrementSteps( PdaRun *pdaRun ); void decrementSteps( PdaRun *pdaRun ); int makeReverseCode( PdaRun *pdaRun ); +void transferReverseCode( PdaRun *pdaRun, Tree *tree ); void initPdaRun( PdaRun *pdaRun, struct ColmProgram *prg, PdaTables *tables, FsmRun *fsmRun, int parserId, long stopTarget, int revertOn, Tree *context ); diff --git a/colm/program.c b/colm/program.c index ecca2e6a..07357ad2 100644 --- a/colm/program.c +++ b/colm/program.c @@ -167,9 +167,10 @@ Program *colmNewProgram( RuntimeData *rtd, int argc, const char **argv ) return prg; } -void colmDeleteProgram( Program *prg ) +int colmDeleteProgram( Program *prg ) { Tree **sp = prg->vm_root; + int exitStatus = prg->exitStatus; #ifdef COLM_LOG_BYTECODE if ( colm_log_bytecode ) { @@ -250,6 +251,8 @@ void colmDeleteProgram( Program *prg ) } free( prg ); + + return exitStatus; } diff --git a/colm/tree.c b/colm/tree.c index 9e48defa..80efedf3 100644 --- a/colm/tree.c +++ b/colm/tree.c @@ -323,7 +323,7 @@ Tree *constructInput( Program *prg ) input->id = LEL_ID_INPUT; input->in = malloc( sizeof(InputStream) ); initInputStream( input->in ); - return input; + return (Tree*)input; } Kid *constructReplacementKid( Tree **bindings, Program *prg, Kid *prev, long pat ); @@ -12,7 +12,9 @@ TESTS=" accum1.lm accum2.lm accum3.lm - accumbt.lm + accumbt1.lm + accumbt2.lm + accumbt3.lm mutualrec.lm argv1.lm argv2.lm diff --git a/test/accumbt.exp b/test/accumbt1.exp index 70c9cd74..70c9cd74 100644 --- a/test/accumbt.exp +++ b/test/accumbt1.exp diff --git a/test/accumbt.lm b/test/accumbt1.lm index 4576e1de..4576e1de 100644 --- a/test/accumbt.lm +++ b/test/accumbt1.lm diff --git a/test/accumbt2.exp b/test/accumbt2.exp index e69de29b..121ed23c 100644 --- a/test/accumbt2.exp +++ b/test/accumbt2.exp @@ -0,0 +1,4 @@ +A1 +A2 +a b c d e ; +extra a extra b extra c extra d extra e diff --git a/test/accumbt3.exp b/test/accumbt3.exp new file mode 100644 index 00000000..74c54485 --- /dev/null +++ b/test/accumbt3.exp @@ -0,0 +1,9 @@ +A1 +discarding: ( (this is a nested comment /*sdf asd_++_stuff) ) +A2 +discarding: ( (this is a nested comment /*sdf asd_++_stuff) ) + +------------ +hello there and this is not +hello there ( (this is a nested comment /*sdf asd_++_stuff) ) and this is not ; + diff --git a/test/accumbt3.in b/test/accumbt3.in new file mode 100644 index 00000000..febc57b1 --- /dev/null +++ b/test/accumbt3.in @@ -0,0 +1 @@ +hello there ( (this is a nested comment /*sdf asd_++_stuff) ) and this is not ; diff --git a/test/accumbt3.lm b/test/accumbt3.lm new file mode 100644 index 00000000..559b2d82 --- /dev/null +++ b/test/accumbt3.lm @@ -0,0 +1,85 @@ +# +# Tokens +# + +# Any single character can be a literal +lex start +{ + # Ignore whitespace. + ignore /[ \t\n\r\v]+/ + + # Open and close id + token id /[a-zA-Z_][a-zA-Z0-9_]*/ + + token open_paren /'('/ + { + parse_stop NC: nested_comment( input ) + print( 'discarding: ' NC '\n' ) + } +} + +# +# Token translation +# + +lex nc_scan +{ + literal '(', ')' + token nc_data /[^()]+/ +} + +def nc_item + [nc_data] +| [nested_comment] + +def nested_comment + ['(' nc_item* ')'] + +def nested [id*] + +global NestedParser: accum<nested> = + cons accum<nested>[] + +lex two +{ + ignore /[ \t]+/ + token word /[a-zA-Z0-9/*+_\-]+/ + token stuff /[a-zA-Z0-9()/*+_\- ]+/ + literal '!', ';', '\n' + + def A1 [] + { print( "A1\n" ) } + + def A2 [] + { print( "A2\n" ) } + + def item + [word] + { + send NestedParser [' '] + send NestedParser [$r1] + send NestedParser [' '] + } + | + [stuff] + { + send NestedParser [' '] + send NestedParser [$r1] + send NestedParser [' '] + } + + def two + [A1 item* '!' '\n'] + | + [A2 item* ';' '\n'] +} + +Two: two = + parse two( stdin ) + +Nested: nested = NestedParser.finish() + +print( '\n------------\n' ) +print( Nested '\n' ) +print( Two '\n' ) + diff --git a/test/nestedcomm.exp b/test/nestedcomm.exp index 5c5368fd..38a6245f 100644 --- a/test/nestedcomm.exp +++ b/test/nestedcomm.exp @@ -1 +1 @@ -hello there ( (this is a nested comment /*sdf;asd_++_stuff) ) and this is not<nested><_repeat_id><id>hello</id><id>there</id><id>and</id><id>this</id><id>is</id><id>not</id></_repeat_id></nested><nested><_repeat_id><id>hello</id><_ignore_0001> </_ignore_0001><id>there</id><_ignore_0001> </_ignore_0001><_notoken></_notoken><_T_nested_comment><_literal_0004>(</_literal_0004><_repeat_nc_item><nc_item><nc_data> </nc_data></nc_item><nc_item><nested_comment><_literal_0004>(</_literal_0004><_repeat_nc_item><nc_item><nc_data>this is a nested comment /*sdf;asd_++_stuff</nc_data></nc_item></_repeat_nc_item><_literal_0005>)</_literal_0005></nested_comment></nc_item><nc_item><nc_data> </nc_data></nc_item></_repeat_nc_item><_literal_0005>)</_literal_0005></_T_nested_comment><_ignore_0001> </_ignore_0001><id>and</id><_ignore_0001> </_ignore_0001><id>this</id><_ignore_0001> </_ignore_0001><id>is</id><_ignore_0001> </_ignore_0001><id>not</id></_repeat_id></nested>hello there ( (this is a nested comment /*sdf;asd_++_stuff) ) and this is not +hello there ( (this is a nested comment /*sdf;asd_++_stuff) ) and this is not<nested><_repeat_id><id>hello</id><id>there</id><id>and</id><id>this</id><id>is</id><id>not</id></_repeat_id></nested><nested><_repeat_id><id>hello</id><_ignore_0001> </_ignore_0001><id>there</id><_ignore_0001> </_ignore_0001><_T_nested_comment><_literal_0004>(</_literal_0004><_repeat_nc_item><nc_item><nc_data> </nc_data></nc_item><nc_item><nested_comment><_literal_0004>(</_literal_0004><_repeat_nc_item><nc_item><nc_data>this is a nested comment /*sdf;asd_++_stuff</nc_data></nc_item></_repeat_nc_item><_literal_0005>)</_literal_0005></nested_comment></nc_item><nc_item><nc_data> </nc_data></nc_item></_repeat_nc_item><_literal_0005>)</_literal_0005></_T_nested_comment><_ignore_0001> </_ignore_0001><id>and</id><_ignore_0001> </_ignore_0001><id>this</id><_ignore_0001> </_ignore_0001><id>is</id><_ignore_0001> </_ignore_0001><id>not</id></_repeat_id></nested>hello there ( (this is a nested comment /*sdf;asd_++_stuff) ) and this is not diff --git a/test/runtests.mk b/test/runtests.mk index 3212ea57..08b37c1e 100755 --- a/test/runtests.mk +++ b/test/runtests.mk @@ -7,10 +7,13 @@ TESTS = \ backtrack1.lm \ backtrack2.lm \ backtrack3.lm \ + binary1.lm \ accum1.lm \ accum2.lm \ accum3.lm \ - accumbt.lm \ + accumbt1.lm \ + accumbt2.lm \ + accumbt3.lm \ mutualrec.lm \ argv1.lm \ argv2.lm \ @@ -68,18 +71,20 @@ TESTS = \ div.lm \ scope1.lm \ export1.lm \ - lhs1.lm \ - binary1.lm + lhs1.lm DIFFS = \ ambig1.diff \ backtrack1.diff \ backtrack2.diff \ backtrack3.diff \ + binary1.diff \ accum1.diff \ accum2.diff \ accum3.diff \ - accumbt.diff \ + accumbt1.diff \ + accumbt2.diff \ + accumbt3.diff \ mutualrec.diff \ argv1.diff \ argv2.diff \ @@ -137,8 +142,7 @@ DIFFS = \ div.diff \ scope1.diff \ export1.diff \ - lhs1.diff \ - binary1.diff + lhs1.diff all: runtests.mk $(DIFFS) $(SUBDIRS) @@ -219,14 +223,30 @@ accum3.out: accum3.bin accum3.bin: accum3.lm ./../colm/colm ./../colm/colm accum3.lm -accumbt.diff: accumbt.out accumbt.exp - @diff -u accumbt.exp accumbt.out > accumbt.diff || ( cat accumbt.diff; rm accumbt.diff ) +accumbt1.diff: accumbt1.out accumbt1.exp + @diff -u accumbt1.exp accumbt1.out > accumbt1.diff || ( cat accumbt1.diff; rm accumbt1.diff ) -accumbt.out: accumbt.bin - ./accumbt.bin > accumbt.out +accumbt1.out: accumbt1.bin + ./accumbt1.bin > accumbt1.out -accumbt.bin: accumbt.lm ./../colm/colm - ./../colm/colm accumbt.lm +accumbt1.bin: accumbt1.lm ./../colm/colm + ./../colm/colm accumbt1.lm +accumbt2.diff: accumbt2.out accumbt2.exp + @diff -u accumbt2.exp accumbt2.out > accumbt2.diff || ( cat accumbt2.diff; rm accumbt2.diff ) + +accumbt2.out: accumbt2.bin + ./accumbt2.bin < accumbt2.in > accumbt2.out + +accumbt2.bin: accumbt2.lm ./../colm/colm + ./../colm/colm accumbt2.lm +accumbt3.diff: accumbt3.out accumbt3.exp + @diff -u accumbt3.exp accumbt3.out > accumbt3.diff || ( cat accumbt3.diff; rm accumbt3.diff ) + +accumbt3.out: accumbt3.bin + ./accumbt3.bin < accumbt3.in > accumbt3.out + +accumbt3.bin: accumbt3.lm ./../colm/colm + ./../colm/colm accumbt3.lm mutualrec.diff: mutualrec.out mutualrec.exp @diff -u mutualrec.exp mutualrec.out > mutualrec.diff || ( cat mutualrec.diff; rm mutualrec.diff ) |