<?xml version="1.0" encoding="utf-8"?>
<!-- Created by Leo: http://leoeditor.com/leo_toc.html -->
<leo_file xmlns:leo="http://leoeditor.com/namespaces/leo-python-editor/1.1" >
<leo_header file_format="2"/>
<globals/>
<preferences/>
<find_panel_settings/>
<vnodes>
<v t="ekr.20070417092935"><vh>Startup</vh>
<v t="ekr.20140716121225.4354"><vh>@@button print-gnx</vh></v>
<v t="ekr.20161123080832.1"><vh>@button make-table</vh></v>
<v t="ekr.20111112092813.4154"><vh>@command cls</vh></v>
<v t="ekr.20101220161557.6011"><vh>@file unitTestStartup.txt</vh></v>
<v t="ekr.20150216110251.11"><vh>@persistence</vh>
<v t="ekr.20170818044949.1"><vh>@data:#@auto</vh></v>
<v t="ekr.20170818044949.2"><vh>@data:#@auto</vh>
<v t="ekr.20170818044949.3"><vh>@gnxs</vh></v>
</v>
<v t="ekr.20170818044949.4"><vh>@data:#@auto</vh></v>
<v t="ekr.20170818044949.5"><vh>@data:#@auto</vh>
<v t="ekr.20170818044949.6"><vh>@gnxs</vh></v>
</v>
<v t="ekr.20171230052539.1"><vh>@data:@auto-org unittest/at-auto-org-line-number-test.py</vh>
<v t="ekr.20171230171407.6"><vh>@gnxs</vh></v>
</v>
<v t="ekr.20171230164813.1"><vh>@data:@auto-otl unittest/at-auto-otl-line-number-test.otl</vh>
<v t="ekr.20171230171407.7"><vh>@gnxs</vh></v>
</v>
<v t="ekr.20171230170021.1"><vh>@data:@auto unittest/at-auto-md-line-number-test.md</vh>
<v t="ekr.20171230171614.10"><vh>@gnxs</vh></v>
</v>
<v t="ekr.20171230171343.1"><vh>@data:@auto unittest/at-auto-line-number-test.py</vh>
<v t="ekr.20171230171407.1"><vh>@gnxs</vh></v>
</v>
<v t="ekr.20171230171343.4"><vh>@data:@auto unittest/at-auto-test.py</vh>
<v t="ekr.20171230171407.3"><vh>@gnxs</vh></v>
</v>
<v t="ekr.20171230171343.6"><vh>@data:@auto unittest/at-auto-unit-test.py</vh>
<v t="ekr.20171230171407.4"><vh>@gnxs</vh></v>
</v>
<v t="ekr.20171230171343.8"><vh>@data:@auto-ctext unittest/at-auto-ctext-test.txt</vh>
<v t="ekr.20171230171407.5"><vh>@gnxs</vh></v>
</v>
<v t="ekr.20171230171343.12"><vh>@data:@auto-rst unittest/at-auto-rst-line-number-test.txt</vh>
<v t="ekr.20171230171407.8"><vh>@gnxs</vh></v>
</v>
</v>
<v t="ekr.20041121151002"><vh>@settings</vh>
<v t="ekr.20080324133327.2"><vh>@bool allow_middle_button_paste = True</vh></v>
<v t="ekr.20161011095551.1"><vh>@bool allow_section_references_in_at_auto = True</vh></v>
<v t="ekr.20101009105124.6195"><vh>@bool put_expansion_bits_in_leo_files = True</vh></v>
<v t="ekr.20140902101931.4478"><vh>@bool qt-use-scintilla = False</vh></v>
<v t="ekr.20161129030232.1"><vh>@bool run-pyflakes-on-write = False</vh></v>
<v t="ekr.20140217055617.4231"><vh>@bool scriptingatscriptnodes = True</vh></v>
<v t="ekr.20180214042153.1"><vh>@bool select-next-after-delete = False</vh></v>
<v t="ekr.20051013162226"><vh>@bool test_setting = True</vh></v>
<v t="ekr.20150602215639.1"><vh>@bool tidy_autobeautify = False</vh></v>
<v t="ekr.20070503064257"><vh>@bool use_chapters = True</vh></v>
<v t="ekr.20150321155210.11"><vh>@bool verbose_check_outline = False</vh></v>
<v t="ekr.20070528100318"><vh>@bool write_strips_blank_lines = False</vh></v>
<v t="ekr.20170415084531.1"><vh>@data import_html_tags</vh></v>
<v t="ekr.20111026111009.3972"><vh>@data import_xml_tags</vh></v>
<v t="ekr.20111123042627.6654"><vh>@enabled-plugins</vh></v>
<v t="ekr.20050328101834"><vh>@page http plugin</vh>
<v t="ekr.20050328101834.1"><vh>@bool http_active = False</vh></v>
<v t="ekr.20050328101834.2"><vh>@int port = 8080</vh></v>
<v t="ekr.20050328101834.3"><vh>@string rst_http_attributename = ''</vh></v>
</v>
<v t="ekr.20111112093605.4679"><vh>@shortcuts</vh></v>
<v t="ekr.20110521073115.3494"><vh>colorizer colors</vh>
<v t="ekr.20110521073115.3495"><vh>@@color keyword3_color = orange</vh></v>
<v t="ekr.20110521073115.3496"><vh>@@color keyword4_color = pink</vh></v>
</v>
<v t="ekr.20111124094121.3941"><vh>Empty @buttons and @commands nodes</vh>
<v t="ekr.20111124094121.3942"><vh>@buttons</vh></v>
<v t="ekr.20111124094121.3943"><vh>@commands</vh></v>
</v>
<v t="ekr.20171126153138.1"><vh>Required for reformat-paragraph tests</vh>
<v t="ekr.20171126152936.1"><vh>@int page_width = 80</vh></v>
<v t="ekr.20171126153044.1"><vh>@int tab_width = -4</vh></v>
</v>
<v t="ekr.20131111155830.4249"><vh>Vim settings</vh>
<v t="ekr.20131111155830.4250"><vh>@@@data vim-control-character-commands</vh></v>
<v t="ekr.20131111155830.4251"><vh>@data vim-command-tails</vh></v>
<v t="ekr.20131111155830.4252"><vh>@data vim-commands</vh></v>
<v t="ekr.20131111155830.4253"><vh>@data vim-motions</vh></v>
<v t="ekr.20131111155830.4254"><vh>@data vim-motion-tails</vh></v>
</v>
</v>
<v t="ekr.20100123172713.5114"><vh>Scripts</vh>
<v t="ekr.20100102164959.5088"><vh>Count pages</vh></v>
<v t="ekr.20100123172713.5116"><vh>Clean all tnodeLists</vh></v>
<v t="ekr.20070217065840"><vh>Scripts that make unit tests</vh>
<v t="ekr.20070217065840.1"><vh>@@command make-test @key = Alt-5</vh></v>
<v t="ekr.20070217065840.2"><vh>@@command do-before @key = Alt-6</vh>
<v t="ekr.20070217065840.3"><vh>getSel</vh></v>
<v t="ekr.20070217065840.4"><vh>findNodes</vh></v>
<v t="ekr.20070217065840.5"><vh>putSelectionInHeadline</vh></v>
</v>
<v t="ekr.20070217065840.6"><vh>@@command do-after @key = Alt-7</vh>
<v t="ekr.20070217072822"><vh>getSel</vh></v>
<v t="ekr.20070217065840.8"><vh>findNodes</vh></v>
<v t="ekr.20070217065840.9"><vh>putSelectionInHeadline</vh></v>
</v>
</v>
<v t="ekr.20070113145100"><vh>Create chinese folder</vh></v>
<v t="ekr.20071113140035"><vh>Find unique @ test nodes</vh></v>
<v t="ekr.20091206090247.5060"><vh>Clear all uA's, tnodeLists, etc.</vh>
<v t="ekr.20091206090247.5061"><vh>Clean unused tnodeLists</vh></v>
<v t="ekr.20091206090247.5062"><vh>Clear all timestamps</vh></v>
<v t="ekr.20091206090247.5063"><vh>Clear all uAs (unknown attributes)</vh></v>
</v>
</v>
</v>
<v t="ekr.20051012104957"><vh>@ignore Docs</vh>
<v t="bwmulder.20050108100437.1"><vh>How to run unit tests</vh></v>
<v t="ekr.20050618061835"><vh>How to use the @test directive, by Roger Erens</vh>
<v t="ekr.20050618061835.1"><vh>Intro</vh>
<v t="ekr.20050618061835.2"><vh>@url http://www.onlamp.com/pub/a/python/2005/02/03/tdd_pyunit2.html</vh></v>
</v>
<v t="ekr.20050618061835.3"><vh>Preparations: adding a button</vh>
<v t="ekr.20050618061835.4"><vh>@@button Do @test</vh></v>
</v>
<v t="ekr.20050618061835.5"><vh>Alpha</vh>
<v t="ekr.20050618061835.6"><vh>@test my first Leo test</vh></v>
<v t="ekr.20050618061835.7"><vh>output on the console</vh></v>
</v>
<v t="ekr.20050618061835.8"><vh>Bravo</vh>
<v t="ekr.20050618061835.9"><vh>@@test my second Leo test</vh></v>
<v t="ekr.20050618061835.10"><vh>output on the console</vh></v>
</v>
<v t="ekr.20050618061835.11"><vh>It takes two to tango</vh>
<v t="ekr.20050618061835.6"></v>
<v t="ekr.20050618061835.9"></v>
<v t="ekr.20050618061835.12"><vh>output on the console</vh></v>
</v>
<v t="ekr.20050618061835.13"><vh>Life gets more interesting</vh>
<v t="ekr.20050618061835.14"><vh>@@test koekiemonster.wants()</vh>
<v t="ekr.20050618061835.15"><vh>input data</vh></v>
<v t="ekr.20050618061835.16"><vh>expected result</vh></v>
</v>
<v t="ekr.20050618061835.17"><vh>output on the console</vh></v>
<v t="ekr.20050618061835.18"><vh>output on the console using print statements</vh></v>
</v>
<v t="ekr.20050618061835.19"><vh>How about @suite?</vh></v>
<v t="ekr.20050618061835.20"><vh>Final remarks</vh></v>
</v>
<v t="ekr.20111211094936.3970"><vh>@ignore To do</vh>
<v t="ekr.20111115080347.3872"><vh>To do: tests of the high-level interface</vh>
<v t="ekr.20100131171342.5478"><vh>@@@test that log and body implements high-level interface</vh></v>
</v>
<v t="ekr.20100131171342.5473"><vh>Tk gui tests</vh>
<v t="ekr.20100131171342.5474"><vh>@test leoBody is subset of leoTkBody</vh></v>
<v t="ekr.20100131171342.5475"><vh>@test leoFrame is subset of leoTkFrame</vh></v>
<v t="ekr.20100131171342.5476"><vh>@test leoGui is subset of leoTkGui</vh></v>
<v t="ekr.20100131171342.5477"><vh>@test leoTree is subset of leoTkTree</vh></v>
</v>
<v t="ekr.20111125183140.3952"><vh>@test ic.createOutline changes back-slashes to slashes</vh></v>
<v t="ekr.20111125182408.3947"><vh>@test ic.createImportParent changes back-slashes to slashes</vh></v>
</v>
</v>
<v t="ekr.20071113203234"><vh>@ignore Test files</vh>
<v t="ekr.20111214104615.3942"><vh>@@@auto unittest/at-auto-section-ref-test.py</vh>
<v t="ekr.20161011092326.7"><vh>at-auto-section-ref-test declarations</vh></v>
</v>
<v t="ekr.20161130041921.1"><vh>@@auto-rst unittest/at-auto-rst-line-number-test.rst</vh>
<v t="ekr.20161228071435.1"><vh>@nosent c:\test\bug-354-test.py</vh>
<v t="ekr.20161228071822.1"><vh>&lt;&lt; u:1 &gt;&gt;</vh></v>
<v t="ekr.20161228071836.1"><vh>&lt;&lt; ue:1 &gt;&gt;</vh></v>
<v t="ekr.20161228071849.1"><vh>&lt;&lt; u:2 &gt;&gt;</vh></v>
<v t="ekr.20161228071905.1"><vh>&lt;&lt; ue: 2&gt;&gt;</vh></v>
</v>
</v>
<v t="ekr.20110615130436.3317"><vh>@@file nonexistent-directory/orphan-bit-test.txt</vh></v>
<v t="ekr.20080907122804.1"><vh>@@shadow unittest/at-shadow-line-number-test.py</vh>
<v t="ekr.20150208213643.15"><vh>spam</vh></v>
<v t="ekr.20150208213643.16"><vh>eggs</vh></v>
</v>
<v t="ekr.20090704085350.5014"><vh>@@shadow unittest/at-shadow-test.py</vh>
<v t="ekr.20150208213643.12"><vh>spam</vh></v>
<v t="ekr.20150208213643.13"><vh>eggs</vh></v>
</v>
<v t="ekr.20111213122041.3930"><vh>@@shadow unittest/at-shadow-unlink-clones.py</vh>
<v t="ekr.20150208213643.18"><vh>Node 1</vh></v>
<v t="ekr.20150208213643.19"><vh>Node 2</vh></v>
</v>
<v t="ekr.20090704085350.5028"><vh>@asis unittest/at-asis-test.py</vh>
<v t="ekr.20090704085350.5029"><vh>spam</vh></v>
<v t="ekr.20090704085350.5030"><vh>eggs</vh></v>
</v>
<v t="ekr.20080904084223.1"><vh>@auto unittest/at-auto-line-number-test.py</vh>
<v t="ekr.20170504122721.1"><vh>at_auto_child</vh></v>
</v>
<v t="ekr.20150626101842.1"><vh>@auto unittest/at-auto-md-line-number-test.md</vh>
<v t="ekr.20171230171138.1"><vh>!Declarations</vh></v>
<v t="ekr.20170504122721.2"><vh>section 1</vh></v>
<v t="ekr.20170504122721.3"><vh>section 2</vh></v>
</v>
<v t="ekr.20090704085350.5056"><vh>@auto unittest/at-auto-test.py</vh>
<v t="ekr.20170504122721.4"><vh>spam</vh></v>
<v t="ekr.20170504122721.5"><vh>eggs</vh></v>
</v>
<v t="ekr.20110610122533.3407"><vh>@auto unittest/at-auto-unit-test.py</vh>
<v t="ekr.20170504122721.6"><vh>class class1</vh>
<v t="ekr.20170504122721.7"><vh>class1_method1</vh></v>
<v t="ekr.20170504122721.8"><vh>class1_method2</vh></v>
</v>
<v t="ekr.20170504122721.9"><vh>class class2</vh>
<v t="ekr.20170504122721.10"><vh>class2_method1</vh></v>
<v t="ekr.20170504122721.11"><vh>class2_method2</vh></v>
</v>
</v>
<v t="ekr.20161130052935.1"><vh>@auto-ctext unittest/at-auto-ctext-test.txt</vh>
<v t="ekr.20170504122721.12"><vh>A level one node</vh></v>
<v t="ekr.20170504122721.13"><vh>Another level one node</vh>
<v t="ekr.20170504122721.14"><vh>A level 2 node</vh></v>
</v>
</v>
<v t="ekr.20150626101627.1"><vh>@auto-org unittest/at-auto-org-line-number-test.org</vh>
<v t="ekr.20170504122721.15"><vh>section 1</vh></v>
<v t="ekr.20170504122721.16"><vh>section 2</vh></v>
</v>
<v t="ekr.20150626100719.1"><vh>@auto-otl unittest/at-auto-otl-line-number-test.otl</vh>
<v t="ekr.20171230171355.20"><vh>First line.</vh></v>
<v t="ekr.20170504122721.17"><vh>section 1</vh></v>
<v t="ekr.20170504122721.18"><vh>section 2</vh></v>
</v>
<v t="ekr.20150626093745.1"><vh>@auto-rst unittest/at-auto-rst-line-number-test.txt</vh></v>
<v t="ekr.20160403143048.1"><vh>@clean unittest/at-clean-line-number-test.c</vh>
<v t="ekr.20160403143048.2"><vh>spam</vh></v>
<v t="ekr.20160403150121.1"><vh>eggs</vh></v>
</v>
<v t="ekr.20160403143130.1"><vh>@clean unittest/at-clean-line-number-test.py</vh>
<v t="ekr.20160403150216.1"><vh>spam</vh></v>
<v t="ekr.20160403150222.1"><vh>eggs</vh></v>
</v>
<v t="ekr.20170401122024.7"><vh>@file C:/leo.repo/leo-editor/leo/test/unittest/input/cweave.w</vh></v>
<v t="ekr.20160403123754.1"><vh>@file unittest/at-file-line-number-test.c</vh>
<v t="ekr.20160403123754.2"><vh>at-file-child</vh></v>
</v>
<v t="ekr.20080904102243.2"><vh>@file unittest/at-file-line-number-test.py</vh>
<v t="ekr.20080904102243.3"><vh>at-file-child</vh></v>
</v>
<v t="ekr.20111021115306.3697"><vh>@file unittest/tex-error.tex</vh>
<v t="ekr.20111021115306.3711"><vh>&lt;&lt; Document &gt;&gt;</vh></v>
</v>
<v t="ekr.20130912092638.4150"><vh>@file unittest/utf-16-test.txt</vh></v>
<v t="ekr.20080907123324.2"><vh>@nosent unittest/at-nosent-line-number-test.py</vh>
<v t="ekr.20080907123324.3"><vh>spam</vh></v>
<v t="ekr.20160403152507.1"><vh>eggs</vh></v>
</v>
<v t="ekr.20090704085350.5034"><vh>@nosent unittest/at-nosent-test.py</vh>
<v t="ekr.20090704085350.5035"><vh>spam</vh></v>
<v t="ekr.20090704085350.5036"><vh>eggs</vh></v>
</v>
<v t="ekr.20100731163237.5782"><vh>@thin unittest/at-thin-html-test.html</vh>
<v t="ekr.20100731163237.5783"><vh>&lt;&lt; a section reference &gt;&gt;</vh></v>
</v>
<v t="ekr.20080905130723.3"><vh>@thin unittest/at-thin-line-number-test.py</vh>
<v t="ekr.20080905130723.4"><vh>at-thin-child</vh></v>
</v>
<v t="ekr.20090704085350.5022"><vh>@thin unittest/at-thin-test.py</vh>
<v t="ekr.20090704085350.5023"><vh>spam</vh></v>
<v t="ekr.20090704085350.5024"><vh>eggs</vh></v>
</v>
<v t="ekr.20070627082044.811"><vh>@thin unittest/batchTest.py</vh></v>
<v t="ekr.20070627082044.808"><vh>@thin unittest/errorTest.py</vh></v>
</v>
<v t="ekr.20170101134417.1"><vh>@ignore Slow tests</vh>
<v t="ekr.20161224111342.1"><vh>@test import all Leo's core files</vh></v>
</v>
<v t="ekr.20101220161557.6016"><vh>Active Unit Tests</vh>
<v t="edward.20160314170027.56" descendentVnodeUnknownAttributes="7d7100285808000000302e362e31362e3371017d71025808000000616e6e6f7461746571037d7104735808000000302e362e31382e3871057d71065808000000616e6e6f7461746571077d7108735809000000302e362e31392e343171097d710a58090000006d795f706c7567696e710b580300000076616c710c735808000000302e362e32372e37710d7d710e5808000000616e6e6f74617465710f7d711073752e"><vh>@file activeUnitTests.txt</vh></v>
</v>
<v t="ekr.20161204040924.1"><vh>@ignore Failing unit tests</vh>
<v t="ekr.20161130041921.1"></v>
<v t="ekr.20161228071915.1"><vh>@@auto c:\test\bug-354-test.py</vh>
<v t="ekr.20161228072352.1"><vh>bug-354-test declarations</vh></v>
</v>
<v t="ekr.20150626093653.1"><vh>@@test find_file_line: @auto-rst</vh></v>
<v t="ekr.20160403143643.1"><vh>@@test find_file_line: @clean-c</vh></v>
<v t="ekr.20160403143655.1"><vh>@@test find_file_line: @clean-python</vh></v>
<v t="ekr.20161130051657.1"><vh>@test ctext</vh></v>
<v t="ekr.20150626101920.1"><vh>@test find_file_line: @auto-md</vh></v>
<v t="ekr.20161130053149.1"><vh>COPY @auto-ctext unittest/at-auto-ctext-test.txt</vh></v>
<v t="ekr.20161103075725.1"><vh>Python failing tests: parse-body</vh>
<v t="ekr.20140206132559.4567"><vh>@@@test ic.parse-body</vh>
<v t="ekr.20150919073819.1"><vh>copy</vh></v>
<v t="ekr.20140206132559.4560"><vh>before</vh>
<v t="ekr.20161112061414.1"><vh>class aClass</vh>
<v t="ekr.20161112061414.2"><vh>__init__</vh></v>
<v t="ekr.20161112061414.3"><vh>spam</vh></v>
</v>
<v t="ekr.20161112061414.4"><vh>bClass = aClass</vh></v>
</v>
<v t="ekr.20140206132559.4564"><vh>expected</vh>
<v t="ekr.20150919074321.1"><vh>class aClass</vh>
<v t="ekr.20150919074321.2"><vh>__init__</vh></v>
<v t="ekr.20150919074321.3"><vh>spam</vh></v>
</v>
</v>
</v>
<v t="ekr.20150919074122.1"><vh>@@@test ic.parse-body mypy</vh>
<v t="ekr.20150919074220.1"><vh>copy</vh></v>
<v t="ekr.20150919074132.1"><vh>before</vh>
<v t="ekr.20161112061406.1"><vh>class TypeJoinVisitor</vh></v>
</v>
<v t="ekr.20150919074154.1"><vh>expected</vh>
<v t="ekr.20150919074211.1"><vh>class TypeJoinVisitor</vh>
<v t="ekr.20150919074211.2"><vh>__init__</vh></v>
<v t="ekr.20150919074211.3"><vh>visit_unbound_type</vh></v>
<v t="ekr.20150919074211.4"><vh>visit_union_type</vh></v>
<v t="ekr.20150919074211.5"><vh>visit_error_type</vh></v>
<v t="ekr.20150919074211.6"><vh>visit_type_list</vh></v>
<v t="ekr.20150919074211.7"><vh>visit_any</vh></v>
<v t="ekr.20150919074211.8"><vh>visit_void</vh></v>
<v t="ekr.20150919074211.9"><vh>visit_none_type</vh></v>
<v t="ekr.20150919074211.10"><vh>visit_erased_type</vh></v>
<v t="ekr.20150919074211.11"><vh>visit_type_var</vh></v>
<v t="ekr.20150919074211.12"><vh>visit_instance</vh></v>
<v t="ekr.20150919074211.13"><vh>visit_callable_type</vh></v>
<v t="ekr.20150919074211.14"><vh>visit_overloaded</vh></v>
<v t="ekr.20150919074211.15"><vh>visit_tuple_type</vh></v>
<v t="ekr.20150919074211.16"><vh>join</vh></v>
<v t="ekr.20150919074211.17"><vh>default</vh></v>
</v>
</v>
</v>
</v>
<v t="ekr.20090529115704.4557"><vh>@test x.makeShadowDirectory</vh>
<v t="ekr.20090529115704.4558"><vh>deleteShadowDir</vh></v>
</v>
</v>
<v t="ekr.20170504122245.1"><vh>@ignore recent</vh>
<v t="ekr.20170712045534.1"><vh>#512: Unit tests that fail for others</vh>
<v t="ekr.20170712053944.1"><vh>Vitalije</vh>
<v t="ekr.20111121224307.3934"><vh>@test k.handleDefaultChar from log pane</vh></v>
</v>
<v t="ekr.20170712045644.1"><vh>Terry's failures</vh>
<v t="ekr.20090529115704.4557"></v>
<v t="ekr.20100131171342.5485"><vh>@test all commands have an event arg</vh></v>
<v t="ekr.20170712132824.1"><vh>add/delete comments</vh>
<v t="ekr.20120309155126.3949"><vh>@test add comments with multiple @language directives</vh>
<v t="ekr.20120309155126.3950"><vh>rest and python</vh></v>
</v>
<v t="ekr.20170712135224.1"><vh>@test delete comments with multiple @language directives</vh>
<v t="ekr.20170712135224.2"><vh>rest and python</vh></v>
</v>
<v t="ekr.20111112171235.3854"><vh>@test add html comments</vh>
<v t="ekr.20170128023431.1"><vh>@language html</vh>
<v t="ekr.20111112171235.3855"><vh>html</vh></v>
</v>
</v>
<v t="ekr.20170712132900.1"><vh>@test delete html comments</vh>
<v t="ekr.20170712132933.1"><vh>@language html</vh>
<v t="ekr.20170712132933.2"><vh>html</vh></v>
</v>
</v>
<v t="ekr.20170712134334.1"><vh>@test add python comments</vh>
<v t="ekr.20170712134334.2"><vh>python</vh></v>
</v>
<v t="ekr.20170712134948.1"><vh>@test delete python comments</vh>
<v t="ekr.20170712134948.3"><vh>python</vh></v>
</v>
</v>
<v t="ekr.20170701153730.1"><vh>rst import test that fail for Terry</vh>
<v t="ekr.20140725132959.4593"><vh>@test rST import test: simple</vh></v>
<v t="ekr.20090529141856.4786"><vh>@test rST import test: no double-underlines</vh></v>
<v t="ekr.20090529141856.4785"><vh>@test rST import test</vh></v>
<v t="ekr.20090529141856.4789"><vh>@test rST import test: trailing whitespace</vh></v>
<v t="ekr.20090529141856.4788"><vh>@test rST import test: long overlines</vh></v>
<v t="ekr.20090529141856.4787"><vh>@test rST import test: long underlines</vh></v>
<v t="ekr.20161129104243.1"><vh>@test leo_rst</vh></v>
</v>
</v>
<v t="ekr.20170712140759.1"><vh>xgid failures</vh>
<v t="ekr.20111124090010.3939"><vh>@test g.app.config @buttons and @commands logic</vh></v>
<v t="ekr.20111123214629.3941"><vh>@@test unbound Alt-9 key is completely ignored</vh></v>
</v>
<v t="ekr.20160523094102.1"><vh>@test leoAst traverser classes</vh></v>
</v>
<v t="ekr.20171126172628.1"><vh>Tests that fail with console gui</vh>
<v t="ekr.20140822063016.4474"><vh>@test zzz force a quit</vh></v>
<v t="ekr.20090306091634.1"><vh>@test print redraw count</vh></v>
</v>
</v>
</vnodes>
<tnodes>
<t tx="bwmulder.20050108100437.1">@killcolor

Running unit tests from test.leo is easy, provided you have enabled the
Scripting plugin. When this plugin is enabled Leo will create a blue 'script
button' in the icon bar called 'unit test'. 

- To run all unit tests, select the node in test.leo called 'Unit tests...',
then do &lt;alt-4&gt; 

- To run a single test, select an @test node and do &lt;alt-4&gt;.

- To run a suite of tests, select an @suite node and do &lt;alt-4&gt;.

- To run any other collection of tests, create an outline containing those @test
or @suite nodes, select the root of that tree and do &lt;alt-4&gt; .

Several nodes in the tree @thin ../src/leoTest.py (in test.leo) contain support
code for @test, @suite, etc. so if you want all the gory details you can read
the code. It's not complicated: Leo creates UnitTest classes automatically whose
run method is the body of the @suite or @test node.
</t>
<t tx="ekr.20041121151002"># Many of these are required for unit tests.
# Do not change them without running all unit tests.</t>
<t tx="ekr.20050328101834"></t>
<t tx="ekr.20050328101834.1"></t>
<t tx="ekr.20050328101834.2"></t>
<t tx="ekr.20050328101834.3"></t>
<t tx="ekr.20050618061835">@killcolor

Here is a tutorial written by Roger Erens.

Version Date        LeoID       Remarks
------- ----        -----       -------
0.1     20050519    rogererens  Initial version</t>
<t tx="ekr.20050618061835.1">So you think "Well, since I've written this piece of funky Python software, and everybody keeps saying how useful unit testing is, I really ought to start using unit tests." And since Leo's creator has said countless times in the Leo forums how easy unit testing in Leo is, a few uncomplicated examples might help convince you that he's not spamming.

Beware: this How-To should be the last time that you write tests AFTER having written your funky software! Test Driven Development dictates that tests have to be prepared BEFORE you get down to writing your actual code. See a nice tutorial on O'Reilly's website (url given in the descendant node).</t>
<t tx="ekr.20050618061835.10">Start of Do @test
F
======================================================================
FAIL: @test my second Leo test

----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Documents and Settings\re1705\My Documents\PythonStuff\leo\src\leoTes
t.py", line 148, in runTest
    exec script + '\n' in {'c':c,'g':g,'p':p}
  File "&lt;string&gt;", line 3, in ?
AssertionError

----------------------------------------------------------------------
Ran 1 test in 0.010s

FAILED (failures=1)
End of Do @test</t>
<t tx="ekr.20050618061835.11">A real fun feature of Leo is that Leo saves you from having to select each and single @test node and press the 'Do @test' button to obtain testing results. Just collecting the @test nodes under an organizing node, selecting that organizing node, and pressing the 'Do @test' button will suffice.
Of course, this was one of the key ideas of unit testing, but it's nice to see it being implemented by Leo so smoothly!

So, press the button while having this node selected, and see if your console's output matches the third child node more or less. </t>
<t tx="ekr.20050618061835.12">Start of Do @test
.F
======================================================================
FAIL: @test my second Leo test

----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\leo\src\leoTest.py", line 148, in runTest
    exec script + '\n' in {'c':c,'g':g,'p':p}
  File "&lt;string&gt;", line 3, in ?
AssertionError

----------------------------------------------------------------------
Ran 2 tests in 0.040s

FAILED (failures=1)
End of Do @test</t>
<t tx="ekr.20050618061835.13">Okay: so you've seen now some simple stand-alone tests to get your toes wet.
Now, we get to the 'grande finale' and see real-life usage of the @test nodes.

The @test child node below illustrates the following points:

1.  The node imports the module to test (and keeps it up to date by reloading it).
2.  It also obtains data to use as input and referral. This is what you might call
    the setUp methods in traditional unit tests. If more tests need the same data,
    you can put the nodes in a central place where all the @test nodes can find
    them. Likewise, common code for several unit tests might be collected in a
    central place.
3.  Comparable with the traditional unit tests' tearDown method, some statements
    can follow the test itself, if neccessary. You might also consider using a
    try/finally construct.
4.  I have used g.es() statements instead of print statements, since the latter
    clutter the console. Try replacing the g.es() statements by corresponding
    print statements to see what I mean. With more than one @test node being tested,
    this will give quite a dreadful look.</t>
<t tx="ekr.20050618061835.14">@color

try:
    # SETUP
    import koekiemonster # this module defines a function want() which we want to test
    reload(koekiemonster) # changes in koekiemonster need to propagate to the test

    # obtaining the input parameter for the function
    inputNode = p.firstChild()
    inputData = inputNode.b
    
    # obtaining the expected result from the function with above input parameter
    expectedResultNode = inputNode.next()
    expected = expectedResultNode.b
    
    # execute the function with above input parameter
    result = koekiemonster.wants(inputData)
    
    # TEST
    assert(result == expected)
    
    # TEARDOWN
    g.es("Now it's time to clean up")

except AssertionError:
    # TEARDOWN
    g.es("Oh oh! %s failed:" % p.h.strip())
    g.es("koekiemonster.wants(%s)==%s" % (inputData, result))
    g.es("Expected: %s" % expected)
    raise # pass the exception on to the unit test machinery</t>
<t tx="ekr.20050618061835.15">vegetables</t>
<t tx="ekr.20050618061835.16">Yuck!</t>
<t tx="ekr.20050618061835.17">Start of Do @test
F
======================================================================
FAIL: @test koekiemonster.wants()

----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\leo\src\leoTest.py", line 148, in runTest
    exec script + '\n' in {'c':c,'g':g,'p':p}
  File "&lt;string&gt;", line 22, in ?
AssertionError

----------------------------------------------------------------------
Ran 1 test in 0.070s

FAILED (failures=1)
End of Do @test</t>
<t tx="ekr.20050618061835.18">Start of Do @test
Oh oh: @test koekiemonster.wants() failed:
koekiemonster.wants(vegetables)=Yack!
Expected: Yuck!
F
======================================================================
FAIL: @test koekiemonster.wants()

----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\leo\src\leoTest.py", line 148, in runTest
    exec script + '\n' in {'c':c,'g':g,'p':p}
  File "&lt;string&gt;", line 22, in ?
AssertionError

----------------------------------------------------------------------
Ran 1 test in 0.051s

FAILED (failures=1)
End of Do @test</t>
<t tx="ekr.20050618061835.19">With the aforementioned possibilities of running all @test nodes in a subtree
with a single click on a button, the sharing of input/expected data, and the
sharing of setUp/tearDown code, there hardly seems a need for the @suite nodes.

On Leo's website
(http://webpages.charter.net/edreamleo/scripting.html#unit-testing-with-test-and-suite)
I could still find some extra info on @suite nodes, not found in LeoDocs.leo:

&lt;quote&gt;

Using @suite nodes

Such nodes create a suite of tests. Leo executes the script in these nodes similar to @test nodes, but Leo assumes that the script in an @suite node will do the following:

    * Create a suite of unit tests
    * Put the suite in g.app.scriptDict["suite"]

The difference between @test and @suite is:

    * Leo creates a unit test from script in an @test node by creating an instance of generalTestCase, a subclass of unittest.TestCase.
    * The script in an @suite node creates the test suite.

An @suite nodes allows us to create "legacy" unit tests simply. For example,
rather than rewriting all the reformatParagraph unit tests to use @test nodes, I
simply wrote an @suite node with the following body.

    suite = c.testManager.makeReformatParagraphSuite()
    g.app.scriptDict['suite'] = suite

&lt;/quote&gt;

Well, that's almost, but not quite completely, totally incomprehensible to me.
Fortunately, since Leo's creator removed most of this quote from LeoDocs.leo and
test.leo, this information appears to be outdated
.</t>
<t tx="ekr.20050618061835.2"></t>
<t tx="ekr.20050618061835.20">Don't forget to read the node
Users Guide--&gt;Chapter 7: Scripting Leo with Python--&gt;Unit testing with @test, @suite
in LeoDocs.leo, especially the final notes. Also investigate a little further in test.leo to see some heavy weight lifting.

This concludes my How-To on using @test. It was not written by an expert on (unit) testing;
it is more or less the result of keeping notes on my way to find out how I could get started
with unit tests in Leo. Maybe it's useful for other newbies as well.

Please post any remarks on one of the Leo Fora, and if deemed helpful, I'll be glad to incorporate them into a next version.

Happy testing!</t>
<t tx="ekr.20050618061835.3">Comment by EKR: There is no need to do this if you use test.leo for your unit tests:  just use the unit test button.

From the console, start up a Leo instance. On MS Windows: go to the folder in which you installed Leo 4.3, open up the 'src' folder and double click the 'leo.py' file. Opening a command window, and typing "python leo.py" may be another way.

Use the File--&gt;Open... menu item to open the outline containing your code.

Choose a good-looking position to insert a new node called 'Test zone'. Copy the child node of the node you're reading right now, and paste it as a child node of 'Test zone'. The code it contains is essentially the same as found in the node
Unit tests...--&gt;Do @test
in the file 'test.leo' in the 'test' folder. I just added my 0.2 cents by adding some starting and finishing remarks. Remove them if they clutter your console too much.

Also, make sure that you have an entry 'Scripting' in the 'Plugins' menu (enable the plugin if neccessary). Now is a good time to save your leo file.

With the 'Scripting' plugin (also known as the 'mod_scripting' plugin) enabled, the net effect will be that the next time you open your outline, a blue button with the caption 'Do @test' shows up in the tool bar of Leo, if your screen is wide enough.
To get the button right away in the tool bar, I have to assume that you started Leo with the scripting plugin enabled. In this case, a yellow button with the caption 'script Button' can be pressed while having the node '@button Do @test' selected. Note: the resulting button will not be blue, but pink. Removing a button from the tool bar can be done by right-clicking it.

We'll see the use of this added button soon, but before that, read up on the 'assert' function in the Python manuals, since it is used a lot in testing. It won't be long before you're back here!</t>
<t tx="ekr.20050618061835.4">@color

g.pr("\nStart of Do @test")
c.testManager.doTests(all=False)
g.pr("End of Do @test")</t>
<t tx="ekr.20050618061835.5">The first child node of the node you're reading right now, contains the simplest succeeding test possible.
Select it, and press the button 'Do @test'.
You can find the verbatim result as it got sent to my console in the second child node.

A few things are worth noting here:

1.  The node containing the test must have its headline start with '@test'.
2.  Whenever the 'assert' statement finds out that the expression given to it
    is 'True', a test passes successfully. There are more ways to pass a test, but
    for now, let us stick to the use of assert functions.
3.  A passed test is denoted with a single dot in the output. See the line between
    'Start of Do @test' and the line filled with dashes. This is compatible with the
    way traditional unit testing shows its progress.
4.  Below the dashed line in the output, a summary is printed. This one surely gives
    us a reason to lean back for a moment, and congratualate ourselves with another
    piece of robust, funky code!</t>
<t tx="ekr.20050618061835.6">@color
assert(True)</t>
<t tx="ekr.20050618061835.7">Start of Do @test
.
----------------------------------------------------------------------
Ran 1 test in 0.010s

OK
End of Do @test</t>
<t tx="ekr.20050618061835.8">Now that you've seen a passing test, it should be obvious to imagine how a failing test would look like. See the first child node, and then run it by pressing the 'Do @test' button as before.

Instead of a dot denoting success, we get an 'F' denoting a failure on the line following 'Start of Do @test'. When running a lot of tests, the next part, following the lines filled with '='s, helps to identify which test failed.
It also includes a trace back, but for AssertionErrors, it does not seem to offer much added value to me right now. On second thought: when you have multiple asserts in a test, the trace back can tell you which assert function failed. Still, I would choose for only one assert function per test and get rid of this trace back altogether. This would keep the console much cleaner, IMHO.</t>
<t tx="ekr.20050618061835.9">@color
assert('Spam' == 'Ham')</t>
<t tx="ekr.20051012104957"></t>
<t tx="ekr.20051013162226"></t>
<t tx="ekr.20070113145100"># Not part of cvs distributions, but needed for two unit tests.

dir = g.os_path_join(g.app.loadDir,'..','test','unittest',g.u('chinese\u8116folder'),encoding='utf-8')
s   = g.os_path_join(dir,g.u('chinese\u8116test.leo'),encoding='utf-8')
    
if not g.os_path_exists(dir):
    import os
    os.mkdir(dir)
    g.pr('created chinese folder')
    
if not g.os_path_exists(s):
    f = file(s,'w')
    f.close()
    g.pr('created chinese file')
    
</t>
<t tx="ekr.20070217065840">@nocolor-node

@
To make unit tests, do the following:
    
- Use the make-test script (Alt-5) to create a suboutline for a unit test.
- Put text in the before node, selected desired text, then do the do-before script (Alt-6).
- Execute the command, then do the do-after script (Alt-7).
</t>
<t tx="ekr.20070217065840.1">try:
    p1 = p.insertAfter()
    c.setHeadString(p1,'@test ')
    body = 'c.testManager.runEditCommandTest(c,p)'
    c.setBodyString(p1,body)
    for s in ('work','before','after'):
        p2 = p1.insertAsLastChild()
        c.setHeadString(p2,s)
    p1.expand()
finally:
    c.redraw()
    c.editPosition(p1)</t>
<t tx="ekr.20070217065840.2">@
p should be in tree whose root is a @test node containing 'work', 'before' and
'after' children. The work node should have body text. If all is as expected,
copy the body text the work node to the before node, and represent the selection
range of the work in the headline of the before node.
@c

@others

sel = getSel(c)
top,work,before,after = findNodes(p)
if top and work.b:

    c.setBodyString(before,work.b)
    c.setBodyString(after,'')
    putSelectionInHeadline(c,before,'before',sel)
    c.redraw()
else:
    g.es_print('do-before: not in a proper @test tree')</t>
<t tx="ekr.20070217065840.3">def getSel(c):
    
    w = c.frame.body.bodyCtrl
    i,j= w.getSelectionRange()
    if i == j:
        i = j = w.getInsertPoint()
        sel = (i,i)
    return i,j</t>
<t tx="ekr.20070217065840.4">def findNodes(p):
    
    '''Find the top, work, before and after nodes.
    p should be in tree whose root is a @test node containing
    'work', 'before' and 'after' children.'''
    
    for p in p.self_and_parents_iter():
        if p.h.startswith('@test '):
            break
    top    = p and p.copy()
    work   = top and top.firstChild() 
    before = work and work.next()     
    after  = before and before.next()
    if (
        work   and work.h.startswith('work') and
        before and before.h.startswith('before') and
        after  and after.h.startswith('after')
    ):
        return top,work,before,after
    else:
        return None,None,None,None</t>
<t tx="ekr.20070217065840.5">def putSelectionInHeadline (c,p,prefix,sel):
    
    # g.trace(p.h,repr(sel))

    w = c.frame.body.bodyCtrl
    i,j = sel
    i,j = w.toGuiIndex(i),w.toGuiIndex(j)
    s = '%s sel=%s,%s' % (prefix,i,j)
    c.setHeadString(p,s)
</t>
<t tx="ekr.20070217065840.6">@
p should be in tree whose root is a @test node containing 'work', 'before' and
'after' children. If all is as expected, copy the work node to the after node,
and represent the selection range of the work node in the headline of the after node.
@c

@others

sel = getSel(c)
top,work,before,after = findNodes(p)
if top:
    c.setBodyString(after,work.b)
    putSelectionInHeadline(c,after,'after',sel)
    c.redraw()
else:
    g.es_print('do-after: not in @test tree')</t>
<t tx="ekr.20070217065840.8">def findNodes(p):
    
    '''Find the top, work, before and after nodes.
    p should be in tree whose root is a @test node containing
    'work', 'before' and 'after' children.'''
    
    for p in p.self_and_parents_iter():
        if p.h.startswith('@test '):
            break
    top    = p and p.copy()
    work   = top and top.firstChild()
    before = work and work.next()
    after  = before and before.next()
    if (
        work   and work.h.startswith('work') and
        before and before.h.startswith('before') and
        after  and after.h.startswith('after')
    ):
        return top,work,before,after
    else:
        return None,None,None,None</t>
<t tx="ekr.20070217065840.9">def putSelectionInHeadline (c,p,prefix,sel):
    
    # g.trace(p.h,repr(sel))
    
    w = c.frame.body.bodyCtrl
    i,j = sel
    i,j = w.toGuiIndex(i),w.toGuiIndex(j)
    s = '%s sel=%s,%s' % (prefix,i,j)
    c.setHeadString(p,s)
</t>
<t tx="ekr.20070217072822">def getSel(c):
    
    w = c.frame.body.bodyCtrl
    i,j= w.getSelectionRange()
    if i == j:
        i = j = w.getInsertPoint()
        sel = (i,i)
    return i,j</t>
<t tx="ekr.20070417092935">@nosearch</t>
<t tx="ekr.20070503064257"></t>
<t tx="ekr.20070528100318"># Required to make a typing test work.
</t>
<t tx="ekr.20070627082044.808"># A file that contains functions with errors in them.
# This is used to test error reporting in scripts

@language python
@tabwidth -4

def testIndexError():

    a = []
    b = a[2]

# The next line is used by @test c.checkFileTimeStamp.   
# timestamp: 1231502468.77
</t>
<t tx="ekr.20070627082044.811"># A file to be executed in batch mode as part of unit testing.
# This file is defined in unitTest.leo

@language python
@tabwidth -4

trace = False
import leo.core.leoGlobals as g
path = g.os_path_join(g.app.loadDir,"..","test","unittest","createdFile.txt")
if trace:
    print("batchTest.py: creating: %s" % path)
try:
    with open(path,"w") as f:
        f.write("This file was written by unittest/batchTest.py")
except IOError:
    print("batchTest.py: Can not create: %s" % path)
except Exception:
    print("batchTest.py: unexpected exception creating: %s" % path)
    g.es_exception()
assert g.os_path_exists(path), 'batchTest.py failed'
</t>
<t tx="ekr.20071113140035">fn = g.os_path_abspath(g.os_path_join(g.app.loadDir,'..','core','leoPy.leo'))
assert g.os_path_exists(fn),fn
c1 = c
c2 = g.openWithFileName(fn,old_c=None,enableLog=False)
assert c2
c.frame.bringToFront()
g.app.setLog(c.frame.log)

d1 = {} ; d2 = {}
for c,d in ( (c1,d1),(c2,d2)):
    for p in c.all_unique_positions():
        if p.h.startswith('@test'):
            d[p.h]=p.h

if 0: # not important
    g.pr()
    g.pr('----- Only in unitTest.leo')
    for h in sorted(d1.keys()):
        if not d2.get(h):
            print(h)

print('\n----- Only in leoPy.leo')
for h in sorted(d2.keys()):
    if not d1.get(h):
        print(h)</t>
<t tx="ekr.20071113203234"></t>
<t tx="ekr.20080324133327.2">True: allow linux-like pastes using a mouse's middle button.

Important: this may cause crashes on some platforms.
</t>
<t tx="ekr.20080904084223.1"># changed.
@others
@language python
@tabwidth -4
</t>
<t tx="ekr.20080904102243.2">@language python
@tabwidth -4
@others
</t>
<t tx="ekr.20080904102243.3">def child():
    pass
</t>
<t tx="ekr.20080905130723.3">@language python
@tabwidth -4
@others
</t>
<t tx="ekr.20080905130723.4">def child():
    pass
</t>
<t tx="ekr.20080907122804.1">@others</t>
<t tx="ekr.20080907123324.2">@language python
@tabwidth -4
# before @others: line 1
@others
# last line: line 6</t>
<t tx="ekr.20080907123324.3">def spam(): # line 2
    pass
</t>
<t tx="ekr.20090306091634.1">tree = c.frame.tree
if hasattr(tree,'redrawCount'):
    g.pr('%s: %s' % (p.h,tree.redrawCount))
</t>
<t tx="ekr.20090529115704.4557">import glob
import os
x = c.shadowController
@others
shadow_fn  = x.shadowPathName('unittest/xyzzy/test.py')
shadow_dir = x.shadowDirName('unittest/xyzzy/test.py')
if g.os_path_exists(shadow_fn):
    g.utils_remove(shadow_fn,verbose=True)
    # assert not os.path.exists(shadow_fn),'still exists: %s' % shadow_fn
    if os.path.exists(shadow_fn):
        # Fix bug #512: Just skip this test.
        self.skipTest('Can not delete the directory.')
deleteShadowDir(shadow_dir)
x.makeShadowDirectory(shadow_dir)
assert os.path.exists(shadow_dir)
deleteShadowDir(shadow_dir)
</t>
<t tx="ekr.20090529115704.4558">def deleteShadowDir(shadowDir):

    if g.os_path_exists(shadow_dir):
        files = g.os_path_abspath(g.os_path_join(shadow_dir,"*.*"))
        files = glob.glob(files)
        for z in files:
            if z != shadow_dir:
                # g.trace(z)
                os.unlink(z)
        # g.trace(shadow_dir)
        os.rmdir(shadow_dir)
        assert not os.path.exists(shadow_dir),'still exists: %s' % shadow_dir
</t>
<t tx="ekr.20090529141856.4785">import leo.core.leoImport as leoImport
if leoImport.docutils is None:
    self.skipTest('no docutils')

if 0: # Preamble
    # g.cls()
    if c.isChanged(): c.save()
    import leo.core.leoImport as leoImport
    import leo.plugins.importers.linescanner as linescanner
    import leo.plugins.importers.leo_rst
    import imp
    imp.reload(leo.plugins.importers.linescanner)
    imp.reload(leo.plugins.importers.leo_rst)
    imp.reload(leoImport)
    g.app.loadManager.createAllImporetersData()
    ic = leoImport.LeoImportCommands(c)
else:
    ic = c.importCommands

s = '''\
.. toc

====
top
====

The top section

section 1
---------

section 1, line 1
--
section 1, line 2

section 2
---------

section 2, line 1

section 2.1
~~~~~~~~~~~

section 2.1, line 1

section 2.1.1
.............

section 2.2.1 line 1

section 3
---------

section 3, line 1

section 3.1.1
.............

section 3.1.1, line 1
'''
table = (
    '!Dummy chapter',
    'top',
    'section 1',
    'section 2',
    'section 2.1',
    'section 2.1.1',
    'section 3',
    'placeholder',
    'section 3.1.1',
)
try:
    ic.rstUnitTest(p,s=s,showTree=True)
    if 1:
        root = c.p.lastChild()
        assert root.h.startswith('@@'), root.h
        p2 = root.firstChild()
        for h in table:
            assert p2.h == h, (p2.h, h)
            p2.moveToThreadNext()
        assert not root.isAncestorOf(p2), p2.h # Extra nodes
finally:
    if 1:
        p.deleteAllChildren()
    c.redraw()
</t>
<t tx="ekr.20090529141856.4786">import leo.core.leoImport as leoImport
if leoImport.docutils is None:
    self.skipTest('no docutils')

if 0: # Preamble
    # g.cls()
    if c.isChanged(): c.save()
    import leo.core.leoImport as leoImport
    import leo.plugins.importers.linescanner as linescanner
    import leo.plugins.importers.leo_rst
    import imp
    imp.reload(leo.plugins.importers.linescanner)
    imp.reload(leo.plugins.importers.leo_rst)
    imp.reload(leoImport)
    g.app.loadManager.createAllImporetersData()
    ic = leoImport.LeoImportCommands(c)
else:
    ic = c.importCommands

s = '''\
.. toc

top
====

The top section

section 1
---------

section 1, line 1
--
section 1, line 2

section 2
---------

section 2, line 1

section 2.1
~~~~~~~~~~~

section 2.1, line 1

section 2.1.1
.............

section 2.2.1 line 1

section 3
---------

section 3, line 1

section 3.1.1
.............

section 3.1.1, line 1
'''
table = (
    '!Dummy chapter',
    'top',
    'section 1',
    'section 2',
    'section 2.1',
    'section 2.1.1',
    'section 3',
    'placeholder',
    'section 3.1.1',
)
try:
    ic.rstUnitTest(p,s=s,showTree=True)
    if 1:
        root = c.p.lastChild()
        assert root.h.startswith('@@'), root.h
        p2 = root.firstChild()
        for h in table:
            assert p2.h == h, (p2.h, h)
            p2.moveToThreadNext()
        assert not root.isAncestorOf(p2), p2.h # Extra nodes
finally:
    if 1:
        p.deleteAllChildren()
    c.redraw()
</t>
<t tx="ekr.20090529141856.4787">import leo.core.leoImport as leoImport
if leoImport.docutils is None:
    self.skipTest('no docutils')

if 0: # Preamble
    # g.cls()
    if c.isChanged(): c.save()
    import leo.core.leoImport as leoImport
    import leo.plugins.importers.linescanner as linescanner
    import leo.plugins.importers.leo_rst
    import imp
    imp.reload(leo.plugins.importers.linescanner)
    imp.reload(leo.plugins.importers.leo_rst)
    imp.reload(leoImport)
    g.app.loadManager.createAllImporetersData()
    ic = leoImport.LeoImportCommands(c)
else:
    ic = c.importCommands
s = '''\
.. toc

top
-------------

The top section
'''
table = (
    '!Dummy chapter',
    'top',
)
try:
    ic.rstUnitTest(p,s=s,showTree=True)
    if 1:
        root = c.p.lastChild()
        assert root.h.startswith('@@'), root.h
        p2 = root.firstChild()
        for h in table:
            assert p2.h == h, (p2.h, h)
            p2.moveToThreadNext()
        assert not root.isAncestorOf(p2), p2.h # Extra nodes
finally:
    if 1:
        p.deleteAllChildren()
    c.redraw()
</t>
<t tx="ekr.20090529141856.4788">import leo.core.leoImport as leoImport
if leoImport.docutils is None:
    self.skipTest('no docutils')

if 0: # Preamble
    # g.cls()
    if c.isChanged(): c.save()
    import leo.core.leoImport as leoImport
    import leo.plugins.importers.linescanner as linescanner
    import leo.plugins.importers.leo_rst
    import imp
    imp.reload(leo.plugins.importers.linescanner)
    imp.reload(leo.plugins.importers.leo_rst)
    imp.reload(leoImport)
    g.app.loadManager.createAllImporetersData()
    ic = leoImport.LeoImportCommands(c)
else:
    ic = c.importCommands

s = '''\
.. toc

======
top
======

The top section
'''
table = (
    "!Dummy chapter",
    "top",
)
try:
    ic.rstUnitTest(p,s=s,showTree=True)
    if 1:
        root = c.p.lastChild()
        assert root.h.startswith('@@'), root.h
        p2 = root.firstChild()
        for h in table:
            assert p2.h == h, (p2.h, h)
            p2.moveToThreadNext()
        assert not root.isAncestorOf(p2), p2.h # Extra nodes
finally:
    if 1:
        p.deleteAllChildren()
    c.redraw()
</t>
<t tx="ekr.20090529141856.4789">import leo.core.leoImport as leoImport
if leoImport.docutils is None:
    self.skipTest('no docutils')

s = '''\
.. toc

.. The section name contains trailing whitespace.

======
top 
======

The top section.
'''
table = (
    "!Dummy chapter",
    "top",
)
try:
    c.importCommands.rstUnitTest(p,s=s,showTree=True)
    if 1:
        root = c.p.lastChild()
        assert root.h.startswith('@@'), root.h
        p2 = root.firstChild()
        for h in table:
            assert p2.h == h, (p2.h, h)
            p2.moveToThreadNext()
        assert not root.isAncestorOf(p2), p2.h # Extra nodes
finally:
    if 1:
        p.deleteAllChildren()
    c.redraw()
</t>
<t tx="ekr.20090704085350.5014">@others</t>
<t tx="ekr.20090704085350.5022">@language python
@others
</t>
<t tx="ekr.20090704085350.5023">def spam():
    pass
</t>
<t tx="ekr.20090704085350.5024">def eggs():
    pass
</t>
<t tx="ekr.20090704085350.5028">@language python
@others</t>
<t tx="ekr.20090704085350.5029">def spam():
    pass</t>
<t tx="ekr.20090704085350.5030">def eggs():
    pass</t>
<t tx="ekr.20090704085350.5034">@first
@language python
@others
</t>
<t tx="ekr.20090704085350.5035">def spam():
    pass # Unicode test: Ã after.
</t>
<t tx="ekr.20090704085350.5036">def eggs():
    pass
</t>
<t tx="ekr.20090704085350.5056">@others
@language python
@tabwidth -4
</t>
<t tx="ekr.20091206090247.5060"># Use these with caution.</t>
<t tx="ekr.20091206090247.5061">count = 0
for p in c.all_unique_positions():
    count += 1
    # Empty tnodeLists are not errors because they never get written to the .leo file.
    v = p.v
    if hasattr(v,"tnodeList") and len(v.tnodeList) &gt; 0 and not v.isAnyAtFileNode():
        g.es("deleting tnodeList for " + `v`,color="blue")
        delattr(v,"tnodeList")
        c.setChanged(True)

s = "%d nodes" % count
print(s) ; g.es(s)</t>
<t tx="ekr.20091206090247.5062"># About the only time you should run this script is when:
# - changing the format of timestamps in nodeIndices.setTimestamp or
# - when making a retroactive change to leoID.txt.

if 0: # This is usually a very bad idea.

    for p in c.all_positions():
        p.v.fileIndex = None

    g.es("all timestamps cleared")</t>
<t tx="ekr.20091206090247.5063">doDelete = False
put = g.es_print
for p in c.all_positions():
    if p.v.u:
        put("found v.u:",p.h,
            g.listToString(p.v.u.keys()))
        if doDelete:
            p.v.u = None
put('done') 
c.redraw()</t>
<t tx="ekr.20100102164959.5088">nodes = 0 ; lines = 0
for p in c.all_unique_positions():
    nodes += 1
    lines += len(g.splitLines(p.b))

pages = ((nodes * 10) + lines) / 50
s = "%d nodes,  %d lines, %d pages" % (nodes,lines,pages)
print(s); g.es(s)</t>
<t tx="ekr.20100123172713.5114"></t>
<t tx="ekr.20100123172713.5116">count = 0
for p in c.all_unique_positions():
    count += 1
    # Empty tnodeLists are not errors because they never get written to the .leo file.
    v = p.v
    if hasattr(v,"tnodeList"): # and len(v.tnodeList) &gt; 0 and not v.isAnyAtFileNode():
        g.es("deleting tnodeList for " + `v`,color="blue")
        delattr(v,"tnodeList")
        c.setChanged(True)

s = "%d nodes" % count
print s ; g.es(s)</t>
<t tx="ekr.20100131171342.5473"></t>
<t tx="ekr.20100131171342.5474">if g.app.gui.guiName() == 'tkinter':

    pc = g.app.pluginsController
    tkGui = pc.loadOnePlugin('leo.plugins.tkGui',verbose=False)
    assert(tkGui)

    import leo.core.leoFrame as leoFrame
    import inspect,sys

    baseClass = leoFrame.leoBody
    subClasses  = (tkGui.leoTkinterBody,leoFrame.nullBody)
    baseObject = c.frame.body

    methods = inspect.getmembers(baseClass,inspect.ismethod)
    methodNames = [z[0] for z in methods]

    for name in baseObject.mustBeDefinedOnlyInBaseClass:
        try:
            assert name in methodNames, 'not defined in base class %s.%s' % (baseClass.__name__,name)
        except AssertionError:
            exctype, value = sys.exc_info()[:2]
            print(value)
            raise

    for subClass in subClasses:
        subclassName = subClass.__name__
        for name in methodNames:
            base_func = getattr(baseClass,name)
            sub_func =  getattr(subClass,name)
            try:
                if name in baseObject.mustBeDefinedOnlyInBaseClass:
                    assert base_func.im_func == sub_func.im_func, 'defined in subclass %s.%s' % (subclassName,name)
                if name in baseObject.mustBeDefinedInSubclasses:
                    assert base_func.im_func != sub_func.im_func, 'not defined in subclass %s.%s' % (subclassName,name)
            except AssertionError:
                #raise
                exctype, value = sys.exc_info()[:2]
                print(value)
</t>
<t tx="ekr.20100131171342.5475">if g.app.gui.guiName() == 'tkinter':

    pc = g.app.pluginsController
    tkGui = pc.loadOnePlugin('leo.plugins.tkGui',verbose=False)

    import leo.core.leoFrame as leoFrame
    import inspect

    baseClass = leoFrame.leoFrame
    subClasses  = (tkGui.leoTkinterFrame,leoFrame.NullFrame)
    baseObject = c.frame

    methods = inspect.getmembers(baseClass,inspect.ismethod)
    methodNames = [z[0] for z in methods]

    for name in baseObject.mustBeDefinedOnlyInBaseClass:
        assert name in methodNames, 'not defined in base class %s.%s' % (baseClass.__name__,name)

    for subClass in subClasses:
        subclassName = subClass.__name__
        for name in methodNames:
            base_func = getattr(baseClass,name)
            sub_func =  getattr(subClass,name)
            if name in baseObject.mustBeDefinedOnlyInBaseClass:
                assert base_func.im_func == sub_func.im_func, 'defined in subclass %s.%s' % (subclassName,name)
            if name in baseObject.mustBeDefinedInSubclasses:
                assert base_func.im_func != sub_func.im_func, 'not defined in subclass %s.%s' % (subclassName,name)
</t>
<t tx="ekr.20100131171342.5476">if g.app.gui.guiName() == 'tkinter':

    pc = g.app.pluginsController
    tkGui = pc.loadOnePlugin('leo.plugins.tkGui',verbose=False)

    import leo.core.leoGui as leoGui
    import inspect

    baseClass = leoGui.leoGui
    subClasses  = (tkGui.tkinterGui,) # nullGui can inherit almost all leoGui dummy methods.
    baseObject = g.app.gui

    methods = inspect.getmembers(baseClass,inspect.ismethod)
    methodNames = [z[0] for z in methods]

    for name in baseObject.mustBeDefinedOnlyInBaseClass:
        assert name in methodNames, 'not defined in base class %s.%s' % (baseClass.__name__,name)

    for subClass in subClasses:
        subclassName = subClass.__name__
        for name in methodNames:
            base_func = getattr(baseClass,name)
            sub_func =  getattr(subClass,name)
            try:
                if name in baseObject.mustBeDefinedOnlyInBaseClass:
                    assert base_func.im_func == sub_func.im_func, 'defined in subclass %s.%s' % (subclassName,name)
                if name in baseObject.mustBeDefinedInSubclasses:
                    assert base_func.im_func != sub_func.im_func, 'not defined in subclass %s.%s' % (subclassName,name)
            except AssertionError:
                raise
</t>
<t tx="ekr.20100131171342.5477">if g.app.gui.guiName() == 'tkinter':

    pc = g.app.pluginsController
    tkGui = pc.loadOnePlugin('leo.plugins.tkGui',verbose=False)

    import leo.core.leoFrame as leoFrame
    import inspect

    baseClass = leoFrame.leoTree
    subClasses  = (tkGui.leoTkinterTree,leoFrame.nullTree)
    baseObject = c.frame.tree

    methods = inspect.getmembers(baseClass,inspect.ismethod)
    methodNames = [z[0] for z in methods]

    for name in baseObject.mustBeDefinedOnlyInBaseClass:
        assert name in methodNames, 'not defined in base class %s.%s' % (baseClass.__name__,name)

    for subClass in subClasses:
        subclassName = subClass.__name__
        for name in methodNames:
            base_func = getattr(baseClass,name)
            sub_func =  getattr(subClass,name)
            if name in baseObject.mustBeDefinedOnlyInBaseClass:
                assert base_func.im_func == sub_func.im_func, 'defined in subclass %s.%s' % (subclassName,name)
            if name in baseObject.mustBeDefinedInSubclasses:
                assert base_func.im_func != sub_func.im_func, 'not defined in subclass %s.%s' % (subclassName,name)
</t>
<t tx="ekr.20100131171342.5478">logCtrl = c.frame.log.logCtrl

table = (
    ('mustBeDefinedInSubclasses',logCtrl.mustBeDefinedInSubclasses),
    ('mustBeDefinedInBaseClass',logCtrl.mustBeDefinedOnlyInBaseClass),
    ('mustBeDefined',logCtrl.mustBeDefined),
)

# Check existence.
for tag,aList in table:
    for z in aList:
        assert hasattr(c.frame.log,z),'%s %s %s' % (tag,c.frame.log,z)
        assert hasattr(c.frame.body,z),'%s %s %s' % (tag,c.frame.body,z)

# Check signatures.
import inspect
for tag,aList in table:
    for z in aList:
        func = getattr(c.frame.body.bodyCtrl,z)
        func2 = getattr(c.frame.log.logCtrl,z)
        assert func,z
        assert func2,z
        d1 = inspect.getargspec(func)
        d2 = inspect.getargspec(func2)
        assert d1==d2,'\n%s\n\nd1 %s\n\nd2 %s' % (z,d1,d2)
</t>
<t tx="ekr.20100131171342.5485">import inspect

d = c.commandsDict
keys = sorted(d.keys())
table = ('bookmark', 'quickmove_', 'screen-capture', 'stickynote')
for key in keys:
    continue_flag = False
    for prefix in table:
        if key.startswith(prefix):
            continue_flag = True
            break # These plugins have their own signatures.
    if continue_flag:
        continue
    f = d.get(key)
    # print(key, f.__name__ if f else repr(f))
    # Test true __call__ methods if they exist.
    name = getattr(f,'__name__',None) or repr(f)
    if hasattr(f,'__call__') and inspect.ismethod(f.__call__):
        f = getattr(f,'__call__')
    args, varargs, varkw, defaults = data = inspect.getargspec(f)
    arg0 = len(args) &gt; 0 and args[0]
    arg1 = len(args) &gt; 1 and args[1]
    expected = ('event',)
    message = '\nno event arg for command %s, func: %s\nargs: %s' % (key,name,data)
    assert arg0 in expected or arg1 in expected, message
</t>
<t tx="ekr.20100731163237.5782">@language html

&lt;&lt; a section reference &gt;&gt;

after.
</t>
<t tx="ekr.20100731163237.5783">&lt;p&gt; a paragraph. &lt;/p&gt;
</t>
<t tx="ekr.20101009105124.6195">True (recommended):
    Write "E" attribute bits in &lt;v&gt; elements.
    Leo outlines will record the expansion state of all nodes.

False:
    (Good for files like unitTest.leo)
    Suppress "E" attribute bits in &lt;v&gt; elements.
    Only the ancestors of the presently selected node will
    be expanded when Leo opens an outline.
</t>
<t tx="ekr.20101220161557.6016"># Nov. 2016: 878 tests.
# Nov. 2017: 916 tests, 25 skipped.

# Some tests are disabled when g.app.isExternalUnitTest is True.
# Using self.skipTest(reason) is now preferred.</t>
<t tx="ekr.20110521073115.3494"></t>
<t tx="ekr.20110521073115.3495">builtins, including cython builtins
</t>
<t tx="ekr.20110521073115.3496">cython keywords
</t>
<t tx="ekr.20110610122533.3407">@others
@language python
@tabwidth -4
</t>
<t tx="ekr.20110615130436.3317"># This was used by @test writing a .leo file retains orphan bits.
# but this test is moot because Leo no longer writes orphan bits.</t>
<t tx="ekr.20111021115306.3697">\begin{document}
&lt;&lt; Document &gt;&gt;
% hidden comment
\end{document}
</t>
<t tx="ekr.20111021115306.3711">@ Write master document here...........
</t>
<t tx="ekr.20111026111009.3972"># lowercase xml tags, one per line.

html
body
head
div
table
nodeA
nodeB
</t>
<t tx="ekr.20111112092813.4154">g.cls()</t>
<t tx="ekr.20111112093605.4679"># leoSettings.leo no longer sets any bindings for run-xxx-unit-test.
# These are now EKR's preferred settings everywhere:
# there should be little need to run unit tests externally.

run-selected-unit-tests-locally     = Alt-4
run-marked-unit-tests-locally       = Alt-5
run-all-unit-tests-locally          = Alt-6

# Important: Alt-9 is used by a unit test
</t>
<t tx="ekr.20111112171235.3854">w = c.frame.body.wrapper
p = g.findNodeInTree(c,p,'html')
assert p,'not found: html'
old_indent = c.config.getBool('indent_added_comments',default=True)
table = (
    (
        False,
        '@language html\n&lt;html&gt;\ntext\n&lt;/html&gt;\n',
        '@language html\n&lt;html&gt;\n&lt;!-- text --&gt;\n&lt;/html&gt;\n',
    ),
    (
        True,
        '@language html\n&lt;html&gt;\n    text\n&lt;/html&gt;\n',
        '@language html\n&lt;html&gt;\n    &lt;!-- text --&gt;\n&lt;/html&gt;\n'
    ),
)
try:
    for indent, s1, expected in table:
        # Step 1: set the setting.
        c.config.set(None, 'bool', 'indent_added_comments', indent, warn=False)
        val = c.config.getBool('indent_added_comments')
        assert indent == val, (repr(indent), repr(val))
        # Step 2: set p.b and the insert point.
        c.selectPosition(p)
        p.b = s1
        i = p.b.find('text')
        assert i &gt; -1,'fail1: %s' % (repr(p.b))
        w.setSelectionRange(i,i+4)
        # Step 3: test add-comments
        c.addComments()
        assert p.b == expected, ('indent: %5s got:\n%r\nexpected:\n%r' % (indent, p.b, expected))
finally:
    c.config.set(p, 'bool', 'indent_added_comments', old_indent)
    val = c.config.getBool('indent_added_comments')
    assert old_indent == val, (repr(indent), repr(val))
</t>
<t tx="ekr.20111112171235.3855">@language html
&lt;html&gt;
    &lt;!-- text --&gt;
&lt;/html&gt;
</t>
<t tx="ekr.20111115080347.3872"></t>
<t tx="ekr.20111121224307.3934">if g.app.isExternalUnitTest:
    self.skipTest('Can not be run externally')
if 'Find' not in c.frame.log.frameDict:
    self.skipTest('No Find tab')
elif g.app.gui.guiName().startswith('qt'):
    tabs = ('Log', 'Find')
    log = c.frame.log
    c.bodyWantsFocusNow()
    last_widget = c.frame.body
    for tab in tabs:
        # A small hack: fudge up the widget to pass to the command.
        event = g.bunch(widget=last_widget)
        c.editCommands.cycleAllFocus(event=event)
        assert log.tabName == tab,'expected %s, got %s' % (
            tab,log.tabName)
        last_widget = log.contentsDict.get(tab)
        event = g.bunch(widget=last_widget)
        # This throws exception: LeoQTextBrowser has no attribute logCtrl.
        c.k.handleDefaultChar(event, stroke='a')
else:
    self.skipTest('Requires Qt Gui')
</t>
<t tx="ekr.20111123042627.6654"># Leo loads plugins in the order they appear here.

# **Important**: to change these defaults, put
# an @enabled-plugins node in myLeoSettings.leo.

# Highly-recommended plugins:
plugins_menu.py
free_layout.py # needs to be early
viewrendered.py

# Recommended plugins:
### contextmenu.py
# leo_to_html.py
mod_scripting.py
# nav_qt.py
# quicksearch.py
# stickynotes.py
# todo.py
</t>
<t tx="ekr.20111123214629.3941">from leo.core.leoQt import QtCore,QtGui,QtWidgets
import leo.plugins.qt_events as qt_events
import sys
if g.app.isExternalUnitTest:
    self.skipTest('Can not be run externally')
elif sys.platform.startswith('linux'):
    self.skipTest('linux test')
elif g.app.gui.guiName().startswith('qt'):
    wrapper = c.frame.body.wrapper
    w = wrapper.widget
    assert g.isTextWrapper(wrapper),wrapper
    assert g.isTextWidget(w),w
    filter_obj = qt_events.LeoQtEventFilter(c,w=w)
    g.app.unitTestDict[p.h] = filter_obj
        # keep a pointer to the filter.
    for z in c.k.bindingsDict.keys():
        if z.s == 'Alt+Key-9':
            self.skipTest('Alt-Key-9 is bound')
    if 0: # Too dangerous, and not useful enough.
        # Create an Alt-9 key event.
        ev = QtCore.QEvent
        e = QtGui.QKeyEvent(ev.KeyPress,ord('9'),QtCore.Qt.AltModifier)
        filter_obj.eventFilter(w,e)
        # Assert that handleUnboundChar actually ignored it.
        assert g.app.unitTestDict.get('handleUnboundChar-ignore-alt-or-ctrl')
else:
     self.skipTest('Requires Qt Gui')
</t>
<t tx="ekr.20111124090010.3939">if g.app.isExternalUnitTest:
    self.skipTest('Can not be run externally')
d = g.app.config.unitTestDict # Always created for this unit test.
keys = ('config.doButtons-file-names','config.doCommands-file-names')
for key in keys:
    aList = d.get(key,[])
    for base in ('leoSettings', 'unitTest'):
        for ext in ('.leo', '.db'):
            if base+ext in aList:
                break
        else:
            assert False,'%s not in unitTestDict[%s]' % (base,key)
</t>
<t tx="ekr.20111124094121.3941"># These exist for a unit test.</t>
<t tx="ekr.20111124094121.3942"></t>
<t tx="ekr.20111124094121.3943"></t>
<t tx="ekr.20111125182408.3947">def setup():
    while p.hasChildren():
        p.firstChild().doDelete()

setup()

try:
    files = (r'a\b.c',r'a\b.h',)
    c.importCommands.createImportParent(p,files)
    child = p.firstChild()
    assert child
    assert child.h == 'a/b',child.h
finally:
    setup()</t>
<t tx="ekr.20111125183140.3952">child = p.firstChild()
def setup():
    while p.hasChildren():
        p.firstChild().doDelete()

setup()
try:
    c.importCommands.createOutline(
        fileName=r'a\b\c.xyzzy',
        parent=p,
        atAuto=False,atShadow=False,
        s='test body',
        ext='xyzzy'
    )
    child = p.firstChild()
    assert child
    h = g.os_path_finalize_join(g.app.loadDir,'..','test','a','b','c.xyzzy')
    h = h.replace('\\','/')
    h = '@file ' + h
    # C: vs c: is not relevant here.
    assert child.h.lower() == h.lower(),child.h
finally:
    setup()</t>
<t tx="ekr.20111211094936.3970"></t>
<t tx="ekr.20111213122041.3930">@language python
@tabwidth -4

# Begin

@others

# End
</t>
<t tx="ekr.20111214104615.3942">@language python
@tabwidth -4
@others
# end.
</t>
<t tx="ekr.20120309155126.3949">w = c.frame.body.wrapper
p = g.findNodeInTree(c,p,'rest and python')
assert p,'not found: rest and python'
old_indent = c.config.getBool('indent_added_comments',default=True)
table = (
    (
        False,
        '@language rest\nrest text.\n@language python\ndef spam():\n    pass\n# after',
        '@language rest\nrest text.\n@language python\ndef spam():\n#     pass\n# after',
    ),
    (
        True,
        '@language rest\nrest text.\n@language python\ndef spam():\n    pass\n# after',
        '@language rest\nrest text.\n@language python\ndef spam():\n    # pass\n# after',
    ),
)
try:
    for indent, s1, expected in table:
        # Step 1: set the setting.
        c.config.set(None, 'bool', 'indent_added_comments', indent, warn=False)
        val = c.config.getBool('indent_added_comments')
        assert indent == val, (repr(indent), repr(val))
        # Step 2: set p.b and the insert point.
        c.selectPosition(p)
        p.b = s1
        i = p.b.find('pass')
        assert i &gt; -1,'fail1: %s' % (repr(p.b))
        w.setSelectionRange(i,i+4)
        # Step 3: test add-comments
        c.addComments()
        assert p.b == expected, ('indent: %5s got:\n%r\nexpected:\n%r' % (indent, p.b, expected))
finally:
    c.config.set(p, 'bool', 'indent_added_comments', old_indent)
    val = c.config.getBool('indent_added_comments')
    assert old_indent == val, (repr(indent), repr(val))
</t>
<t tx="ekr.20120309155126.3950">@language rest
rest text.
@language python
def spam():
    # pass
# after
</t>
<t tx="ekr.20130912092638.4150">@first # -*- coding: utf-16 -*-
@encoding utf-16

Test of utf-16.
</t>
<t tx="ekr.20131111155830.4249"></t>
<t tx="ekr.20131111155830.4250"># Not yet...

    &lt;BS&gt;        delete the character in front of the cursor
N   &lt;Del&gt;       delete N characters under and after the cursor
    &lt;Del&gt;       delete the character under the cursor
    &lt;Del&gt;       while entering a count: delete last character
    &lt;Down&gt;      recall newer command-line that starts with current command
    &lt;Esc&gt;       abandon command-line (if 'wildchar' is &lt;Esc&gt;, type it twice)
    &lt;Left&gt;      (motion) cursor left
    &lt;Right&gt;     (motion) cursor right
    &lt;S-Down&gt;    recall newer command-line from history
    &lt;S-Left&gt;    (motion) cursor one word left
    &lt;S-Right&gt;   (motion) cursor one word right
    &lt;S-Up&gt;      recall older command-line from history
    &lt;Up&gt;        recall older command-line that starts with current command

N   CTRL-^                  Edit alternate file N (equivalent to ":e #N").
N   CTRL-A                  add N to the number at or after the cursor
N   CTRL-B                  window N pages Backwards (upwards)
    CTRL-B                  (motion?) cursor to beginning of command-line
    CTRL-BREAK              MS-DOS: during searches: interrupt the search
    CTRL-C                  during searches: interrupt the search
N   CTRL-D                  window N lines Downwards (default: 1/2 window)
N   CTRL-E                  window N lines downwards (default: 1)
    CTRL-E                  (motion?) cursor to end of command-line
N   CTRL-F                  (motion) window N pages Forwards (downwards)
    CTRL-G                  show current file name (with path) and cursor position
N   CTRL-I                  (motion) go to Nth newer position in jump list
    CTRL-K {char1} {char2}  enter digraph
    CTRL-L                  Clear and redraw the screen.
N   CTRL-O                  (motion) go to Nth older position in jump list
N   CTRL-R                  redo last N undone changes
    CTRL-R &lt;0-9a-z"%:-&gt;     insert contents of register &lt;0-9a-z"%:-&gt;
N   CTRL-T                  (motion) Jump back from Nth older tag in tag list
N   CTRL-U                  window N lines Upwards (default: 1/2 window)
    CTRL-U                  remove all characters
    CTRL-V                  highlight blockwise or stop highlighting
    CTRL-V                  start highlighting blockwise   }  highlighted text
    CTRL-V {char}           insert {char} literally
    CTRL-V {number}         enter decimal value of character (up to three digits)
    CTRL-W                  delete the word in front of the cursor
    CTRL-W +                Increase current window height
    CTRL-W -                Decrease current window height
    CTRL-W =                Make all windows equal height
    CTRL-W CTRL-W           Move cursor to window below (wrap)
    CTRL-W CTRL-^           Split window and edit alternate file
    CTRL-W R                Rotate windows upwards
    CTRL-W W                Move cursor to window above (wrap)
    CTRL-W ]                Split window and jump to tag under cursor
    CTRL-W _                Set current window height (default: very high)
    CTRL-W b                Move cursor to bottom window
    CTRL-W c  or :cl[ose]   Make buffer hidden and close window
    CTRL-W f                Split window and edit file name under the cursor
    CTRL-W j                Move cursor to window below
    CTRL-W k                Move cursor to window above
    CTRL-W n  or :new       Create new empty window
    CTRL-W o  or :on[ly]    Make current window only one on the screen
    CTRL-W p                Move cursor to previous active window
    CTRL-W q  or :q[uit]    Quit editing and close window
    CTRL-W r                Rotate windows downwards
    CTRL-W s                Split window into two parts
    CTRL-W t                Move cursor to top window
    CTRL-W x                Exchange current window with next one
N   CTRL-X                  subtract N from the number at or after the cursor
N   CTRL-Y                  window N lines upwards (default: 1)
    CTRL-Z                  Same as ":stop!"
    CTRL-]                  Jump to the tag under cursor, unless changes have been made</t>
<t tx="ekr.20131111155830.4251">char F
char T
char f
char r
char t
letter m
letter q
motion &lt;
motion &gt;
motion c
motion d
motion gU
motion gq
motion gu
motion g~
motion y
pattern /
pattern ?
register @
</t>
<t tx="ekr.20131111155830.4252"># http://tnerual.eriogerg.free.fr/vimqrc.html
vim_0 0
vim_tilda ~
vim_plus +
vim_underscore _
vim_minus -
vim_comma ,
vim_dot .
vim_semicolon ;
vim_lparen (
vim_rparen )
vim_lcurly {
vim_rcurly }
vim_vertical |
vim_backtick `
vim_dollar $
vim_caret ^
vim_percent %
vim_langle &lt;
vim_langle &lt;&lt;
vim_rangle &gt;
vim_rangle &gt;&gt;
vim_pound #
vim_star *
vim_slash /\\n
vim_slash /
vim_question ?\\n
vim_question ?
vim_at @
vim_at @@
vim_dquote "
vim_lsquare [#
vim_lsquare [(
vim_lsquare [*
vim_lsquare [[
vim_lsquare []
vim_lsquare [p
vim_lsquare [{
vim_rsquare ]#
vim_rsquare ])
vim_rsquare ]*
vim_rsquare ][
vim_rsquare ]]
vim_rsquare ]p
vim_rsquare ]}
vim_A A
vim_B B
vim_C C
vim_D D
vim_E E
vim_F F
vim_G G
vim_H H
vim_I I
vim_J J
vim_K K
vim_M M
vim_L L
vim_N N
vim_O O
vim_P P
vim_R R
vim_S S
vim_T T
vim_U U
vim_V V
vim_W W
vim_X X
vim_Y Y
vim_Z ZQ
vim_Z ZZ
vim_a a
vim_b b
vim_c c
vim_d dd
vim_d d
vim_g g~
vim_g g^
vim_g g#
vim_g g$
vim_g g*
vim_g g0
vim_g gD
vim_g gE
vim_g gI
vim_g gU
vim_g ga
vim_g gd
vim_g ge
vim_g gf
vim_g gg
vim_g gj
vim_g gk
vim_g gq
vim_g gs
vim_g gu
vim_g gv
vim_h h
vim_i i
vim_j j
vim_k k
vim_l l
vim_n n
vim_m m
vim_o o
vim_p p
vim_q q
vim_r r
vim_s s
vim_t t
vim_u u
vim_v v
vim_w w
vim_x x
vim_y y
vim_y yy
vim_z z-
vim_z z.
vim_z z&lt;CR&gt;
vim_z zb
vim_z zh
vim_z zl
vim_z zt
vim_z zz
</t>
<t tx="ekr.20131111155830.4253"># CR
# Ctrl-End
# Ctrl-Home
# Ctrl-Left
# Ctrl-M
# Ctrl-N
# Ctrl-P
# Ctrl-Right
# End
# Home
# Shift-Left
# Shift-Right

( 	
)
{
}
[[
[]
][
]]
$
^	
+
,
-
;
_
0
B
E
F
G
T
W
b
e
f
g$
g^
g0
gE
# gEnd
# gHome	
ge
gg
h
j
k
t
w
</t>
<t tx="ekr.20131111155830.4254">char F
char T
char f
char t
</t>
<t tx="ekr.20140206132559.4560">@others
</t>
<t tx="ekr.20140206132559.4564">@others
bClass = aClass
</t>
<t tx="ekr.20140206132559.4567">tm = c.testManager
before   = g.findNodeInTree(c,p,'before')
expected = g.findNodeInTree(c,p,'expected')
assert before,expected
try:
    c.selectPosition(before)
    before.h = 'expected' # To make the compare work.
    c.importCommands.parse_body(before)
    # compare tree.
    assert tm.compareOutlines(before,expected,compareHeadlines=True,tag='',report=True)
    c.undoer.undo()
finally:
    before.h = 'before'
    c.redraw()
</t>
<t tx="ekr.20140217055617.4231"># For a unit test.</t>
<t tx="ekr.20140716121225.4354">print(p.v.gnx)</t>
<t tx="ekr.20140725132959.4593">import leo.core.leoImport as leoImport
if leoImport.docutils is None:
    self.skipTest('no docutils')

s = '''\
.. toc

.. The section name contains trailing whitespace.

=======
Chapter 
=======

The top chapter.
'''
table = (
    "!Dummy chapter",
    "Chapter",
)
try:
    c.importCommands.rstUnitTest(p,s=s,showTree=True)
    if 1:
        root = c.p.lastChild()
        assert root.h.startswith('@@'), root.h
        p2 = root.firstChild()
        for h in table:
            assert p2.h == h, (p2.h, h)
            p2.moveToThreadNext()
        assert not root.isAncestorOf(p2), p2.h # Extra nodes
finally:
    if 1:
        p.deleteAllChildren()
    c.redraw()
</t>
<t tx="ekr.20140822063016.4474">g.app.forceShutdown()
    # Now works with curses gui.
</t>
<t tx="ekr.20140902101931.4478"></t>
<t tx="ekr.20150208213643.12">def spam():
    pass</t>
<t tx="ekr.20150208213643.13">def eggs():
    pass</t>
<t tx="ekr.20150208213643.15">def spam():
    pass</t>
<t tx="ekr.20150208213643.16">def eggs():
    pass</t>
<t tx="ekr.20150208213643.18"># node 1 text A.
</t>
<t tx="ekr.20150208213643.19"># node 2 text B.
</t>
<t tx="ekr.20150216110251.11"># Do not delete this node.
# It is used by unit tests.</t>
<t tx="ekr.20150321155210.11"></t>
<t tx="ekr.20150602215639.1">True: Automatically beautify all @&lt;file&gt; nodes when saving an outline.

# This *must* be False in unitTest.leo!</t>
<t tx="ekr.20150626093653.1"># Not valid for external tests: uses @&lt;file&gt; node.
if g.app.isExternalUnitTest:
    self.skipTest('Can not be run externally')
else:
    trace = True
    h = '@auto-rst unittest/at-auto-rst-line-number-test.rst'
    root = g.findNodeAnywhere(c, h)
    assert root
    assert root.isAtAutoRstNode(), root
    s = c.gotoCommands.get_external_file_with_sentinels(root)
    if trace:
        # g.cls()
        print('get_external_file_with_sentinels returns...')
        # print(''.join(['%3s %r' % (i, s) for i, s in enumerate(g.splitLines(s))]))
        g.printList(g.splitLines(s))
    for n in range(20):
        p, offset, found = c.gotoCommands.find_file_line(n, p=root)
        if found:
            if trace: print('found: %2s %2s %s' % (n, offset, p and p.h))
        else:
            if trace: print('not found: %s' % n)
            assert n == 9, n
            break
</t>
<t tx="ekr.20150626093745.1">@language rest
@tabwidth -4
</t>
<t tx="ekr.20150626100719.1">@language plain
@tabwidth -4
</t>
<t tx="ekr.20150626101627.1">Introduction line 1.
Introduction line 2.
@language plain
@tabwidth -4
</t>
<t tx="ekr.20150626101842.1">@language md
@tabwidth -4
</t>
<t tx="ekr.20150626101920.1">import sys
# Not valid for external tests: uses @&lt;file&gt; node.
if g.app.isExternalUnitTest:
    self.skipTest('Can not be run externally')
elif sys.platform.startswith('win'):
    trace = False
    h = '@auto unittest/at-auto-md-line-number-test.md'
    root = g.findNodeAnywhere(c, h)
    assert root
    s = c.gotoCommands.get_external_file_with_sentinels(root)
    if trace:
        # g.cls()
        print(''.join(['%3s %s' % (i, s) for i, s in enumerate(g.splitLines(s))]))
    for n in range(20):
        p, offset, found = c.gotoCommands.find_file_line(n, p=root)
        if found:
            if trace: print('found: %2s %2s %s' % (n, offset, p and p.h))
        else:
            if trace: print('not found: %s' % n)
            assert n == 7, n
            break
else:
    self.skipTest('Skip on Linux')
</t>
<t tx="ekr.20150919073819.1">class aClass:
    def __init__(self):
        pass
    def spam(self):
        pass
bClass = aClass
</t>
<t tx="ekr.20150919074122.1">tm = c.testManager
before   = g.findNodeInTree(c,p,'before')
expected = g.findNodeInTree(c,p,'expected')
assert before,expected
try:
    c.selectPosition(before)
    before.h = 'expected' # To make the compare work.
    c.importCommands.parse_body(before)
    # compare tree.
    assert tm.compareOutlines(before,expected,compareHeadlines=True,tag='',report=True)
    c.undoer.undo()
finally:
    before.h = 'before'
    c.redraw()
</t>
<t tx="ekr.20150919074132.1">@others
</t>
<t tx="ekr.20150919074154.1">@others
</t>
<t tx="ekr.20150919074211.1">class TypeJoinVisitor(TypeVisitor[Type]):
    """Implementation of the least upper bound algorithm.

    Attributes:
      s: The other (left) type operand.
    """
    @others
</t>
<t tx="ekr.20150919074211.10">def visit_erased_type(self, t: ErasedType) -&gt; Type:
    return self.s

</t>
<t tx="ekr.20150919074211.11">def visit_type_var(self, t: TypeVarType) -&gt; Type:
    if isinstance(self.s, TypeVarType) and (cast(TypeVarType, self.s)).id == t.id:
        return self.s
    else:
        return self.default(self.s)

</t>
<t tx="ekr.20150919074211.12">def visit_instance(self, t: Instance) -&gt; Type:
    if isinstance(self.s, Instance):
        return join_instances(t, cast(Instance, self.s))
    elif isinstance(self.s, FunctionLike):
        return join_types(t, self.s.fallback)
    else:
        return self.default(self.s)

</t>
<t tx="ekr.20150919074211.13">def visit_callable_type(self, t: CallableType) -&gt; Type:
    # TODO: Consider subtyping instead of just similarity.
    if isinstance(self.s, CallableType) and is_similar_callables(
            t, cast(CallableType, self.s)):
        return combine_similar_callables(t, cast(CallableType, self.s))
    elif isinstance(self.s, Overloaded):
        # Switch the order of arguments to that we'll get to visit_overloaded.
        return join_types(t, self.s)
    else:
        return join_types(t.fallback, self.s)

</t>
<t tx="ekr.20150919074211.14">def visit_overloaded(self, t: Overloaded) -&gt; Type:
    # This is more complex than most other cases. Here are some
    # examples that illustrate how this works.
    #
    # First let's define a concise notation:
    #  - Cn are callable types (for n in 1, 2, ...)
    #  - Ov(C1, C2, ...) is an overloaded type with items C1, C2, ...
    #  - Callable[[T, ...], S] is written as [T, ...] -&gt; S.
    #
    # We want some basic properties to hold (assume Cn are all
    # unrelated via Any-similarity):
    #
    #   join(Ov(C1, C2), C1) == C1
    #   join(Ov(C1, C2), Ov(C1, C2)) == Ov(C1, C2)
    #   join(Ov(C1, C2), Ov(C1, C3)) == C1
    #   join(Ov(C2, C2), C3) == join of fallback types
    #
    # The presence of Any types makes things more interesting. The join is the
    # most general type we can get with respect to Any:
    #
    #   join(Ov([int] -&gt; int, [str] -&gt; str), [Any] -&gt; str) == Any -&gt; str
    #
    # We could use a simplification step that removes redundancies, but that's not
    # implemented right now. Consider this example, where we get a redundancy:
    #
    #   join(Ov([int, Any] -&gt; Any, [str, Any] -&gt; Any), [Any, int] -&gt; Any) ==
    #       Ov([Any, int] -&gt; Any, [Any, int] -&gt; Any)
    #
    # TODO: Use callable subtyping instead of just similarity.
    result = []  # type: List[CallableType]
    s = self.s
    if isinstance(s, FunctionLike):
        # The interesting case where both types are function types.
        for t_item in t.items():
            for s_item in s.items():
                if is_similar_callables(t_item, s_item):
                    result.append(combine_similar_callables(t_item, s_item))
        if result:
            # TODO: Simplify redundancies from the result.
            if len(result) == 1:
                return result[0]
            else:
                return Overloaded(result)
        return join_types(t.fallback, s.fallback)
    return join_types(t.fallback, s)

</t>
<t tx="ekr.20150919074211.15">def visit_tuple_type(self, t: TupleType) -&gt; Type:
    if (isinstance(self.s, TupleType) and
            cast(TupleType, self.s).length() == t.length()):
        items = []  # type: List[Type]
        for i in range(t.length()):
            items.append(self.join(t.items[i],
                                   (cast(TupleType, self.s)).items[i]))
        # TODO: What if the fallback types are different?
        return TupleType(items, t.fallback)
    else:
        return self.default(self.s)

</t>
<t tx="ekr.20150919074211.16">def join(self, s: Type, t: Type) -&gt; Type:
    return join_types(s, t)

</t>
<t tx="ekr.20150919074211.17">def default(self, typ: Type) -&gt; Type:
    if isinstance(typ, Instance):
        return object_from_instance(typ)
    elif isinstance(typ, UnboundType):
        return AnyType()
    elif isinstance(typ, Void) or isinstance(typ, ErrorType):
        return ErrorType()
    elif isinstance(typ, TupleType):
        return self.default(typ.fallback)
    elif isinstance(typ, FunctionLike):
        return self.default(typ.fallback)
    elif isinstance(typ, TypeVarType):
        return self.default(typ.upper_bound)
    else:
        return AnyType()
</t>
<t tx="ekr.20150919074211.2">
def __init__(self, s: Type) -&gt; None:
    self.s = s

</t>
<t tx="ekr.20150919074211.3">def visit_unbound_type(self, t: UnboundType) -&gt; Type:
    if isinstance(self.s, Void) or isinstance(self.s, ErrorType):
        return ErrorType()
    else:
        return AnyType()

</t>
<t tx="ekr.20150919074211.4">def visit_union_type(self, t: UnionType) -&gt; Type:
    if is_subtype(self.s, t):
        return t
    else:
        return UnionType(t.items + [self.s])

</t>
<t tx="ekr.20150919074211.5">def visit_error_type(self, t: ErrorType) -&gt; Type:
    return t

</t>
<t tx="ekr.20150919074211.6">def visit_type_list(self, t: TypeList) -&gt; Type:
    assert False, 'Not supported'

</t>
<t tx="ekr.20150919074211.7">def visit_any(self, t: AnyType) -&gt; Type:
    return t

</t>
<t tx="ekr.20150919074211.8">def visit_void(self, t: Void) -&gt; Type:
    if isinstance(self.s, Void):
        return t
    else:
        return ErrorType()

</t>
<t tx="ekr.20150919074211.9">def visit_none_type(self, t: NoneTyp) -&gt; Type:
    if not isinstance(self.s, Void):
        return self.s
    else:
        return self.default(self.s)

</t>
<t tx="ekr.20150919074220.1">class TypeJoinVisitor(TypeVisitor[Type]):
    """Implementation of the least upper bound algorithm.

    Attributes:
      s: The other (left) type operand.
    """

    def __init__(self, s: Type) -&gt; None:
        self.s = s

    def visit_unbound_type(self, t: UnboundType) -&gt; Type:
        if isinstance(self.s, Void) or isinstance(self.s, ErrorType):
            return ErrorType()
        else:
            return AnyType()

    def visit_union_type(self, t: UnionType) -&gt; Type:
        if is_subtype(self.s, t):
            return t
        else:
            return UnionType(t.items + [self.s])

    def visit_error_type(self, t: ErrorType) -&gt; Type:
        return t

    def visit_type_list(self, t: TypeList) -&gt; Type:
        assert False, 'Not supported'

    def visit_any(self, t: AnyType) -&gt; Type:
        return t

    def visit_void(self, t: Void) -&gt; Type:
        if isinstance(self.s, Void):
            return t
        else:
            return ErrorType()

    def visit_none_type(self, t: NoneTyp) -&gt; Type:
        if not isinstance(self.s, Void):
            return self.s
        else:
            return self.default(self.s)

    def visit_erased_type(self, t: ErasedType) -&gt; Type:
        return self.s

    def visit_type_var(self, t: TypeVarType) -&gt; Type:
        if isinstance(self.s, TypeVarType) and (cast(TypeVarType, self.s)).id == t.id:
            return self.s
        else:
            return self.default(self.s)

    def visit_instance(self, t: Instance) -&gt; Type:
        if isinstance(self.s, Instance):
            return join_instances(t, cast(Instance, self.s))
        elif isinstance(self.s, FunctionLike):
            return join_types(t, self.s.fallback)
        else:
            return self.default(self.s)

    def visit_callable_type(self, t: CallableType) -&gt; Type:
        # TODO: Consider subtyping instead of just similarity.
        if isinstance(self.s, CallableType) and is_similar_callables(
                t, cast(CallableType, self.s)):
            return combine_similar_callables(t, cast(CallableType, self.s))
        elif isinstance(self.s, Overloaded):
            # Switch the order of arguments to that we'll get to visit_overloaded.
            return join_types(t, self.s)
        else:
            return join_types(t.fallback, self.s)

    def visit_overloaded(self, t: Overloaded) -&gt; Type:
        # This is more complex than most other cases. Here are some
        # examples that illustrate how this works.
        #
        # First let's define a concise notation:
        #  - Cn are callable types (for n in 1, 2, ...)
        #  - Ov(C1, C2, ...) is an overloaded type with items C1, C2, ...
        #  - Callable[[T, ...], S] is written as [T, ...] -&gt; S.
        #
        # We want some basic properties to hold (assume Cn are all
        # unrelated via Any-similarity):
        #
        #   join(Ov(C1, C2), C1) == C1
        #   join(Ov(C1, C2), Ov(C1, C2)) == Ov(C1, C2)
        #   join(Ov(C1, C2), Ov(C1, C3)) == C1
        #   join(Ov(C2, C2), C3) == join of fallback types
        #
        # The presence of Any types makes things more interesting. The join is the
        # most general type we can get with respect to Any:
        #
        #   join(Ov([int] -&gt; int, [str] -&gt; str), [Any] -&gt; str) == Any -&gt; str
        #
        # We could use a simplification step that removes redundancies, but that's not
        # implemented right now. Consider this example, where we get a redundancy:
        #
        #   join(Ov([int, Any] -&gt; Any, [str, Any] -&gt; Any), [Any, int] -&gt; Any) ==
        #       Ov([Any, int] -&gt; Any, [Any, int] -&gt; Any)
        #
        # TODO: Use callable subtyping instead of just similarity.
        result = []  # type: List[CallableType]
        s = self.s
        if isinstance(s, FunctionLike):
            # The interesting case where both types are function types.
            for t_item in t.items():
                for s_item in s.items():
                    if is_similar_callables(t_item, s_item):
                        result.append(combine_similar_callables(t_item, s_item))
            if result:
                # TODO: Simplify redundancies from the result.
                if len(result) == 1:
                    return result[0]
                else:
                    return Overloaded(result)
            return join_types(t.fallback, s.fallback)
        return join_types(t.fallback, s)

    def visit_tuple_type(self, t: TupleType) -&gt; Type:
        if (isinstance(self.s, TupleType) and
                cast(TupleType, self.s).length() == t.length()):
            items = []  # type: List[Type]
            for i in range(t.length()):
                items.append(self.join(t.items[i],
                                       (cast(TupleType, self.s)).items[i]))
            # TODO: What if the fallback types are different?
            return TupleType(items, t.fallback)
        else:
            return self.default(self.s)

    def join(self, s: Type, t: Type) -&gt; Type:
        return join_types(s, t)

    def default(self, typ: Type) -&gt; Type:
        if isinstance(typ, Instance):
            return object_from_instance(typ)
        elif isinstance(typ, UnboundType):
            return AnyType()
        elif isinstance(typ, Void) or isinstance(typ, ErrorType):
            return ErrorType()
        elif isinstance(typ, TupleType):
            return self.default(typ.fallback)
        elif isinstance(typ, FunctionLike):
            return self.default(typ.fallback)
        elif isinstance(typ, TypeVarType):
            return self.default(typ.upper_bound)
        else:
            return AnyType()
</t>
<t tx="ekr.20150919074321.1">class aClass:
    @others
</t>
<t tx="ekr.20150919074321.2">def __init__(self):
    pass
</t>
<t tx="ekr.20150919074321.3">def spam(self):
    pass
</t>
<t tx="ekr.20160403123754.1">@language c
@tabwidth -4
@others
</t>
<t tx="ekr.20160403123754.2">def child():
    pass
</t>
<t tx="ekr.20160403143048.1">@language c
@tabwidth -4
// before @others // line 1
@others
// last line: line 6
</t>
<t tx="ekr.20160403143048.2">def spam(): // line 2
    pass
</t>
<t tx="ekr.20160403143130.1">@language python
@tabwidth -4
# Before @others: line 1
@others
# Last line: line 6
</t>
<t tx="ekr.20160403143643.1"># Not valid for external tests: uses @&lt;file&gt; node.
if g.app.isExternalUnitTest:
    self.skipTest('Can not be run externally')
else:
    trace = False
    root = p.parent().parent()
    h = '@clean unittest/at-clean-line-number-test.c'
    target = g.findNodeAnywhere(c, h)
    assert target, 'no target'
    s = c.gotoCommands.get_external_file_with_sentinels(target)
    lines = g.splitLines(s)
    g.printList(lines)
    stripped_lines = [z for z in lines if not z.startswith('//@')]
    g.printList(stripped_lines)
    if trace:
        # g.cls()
        print(''.join(['%3s %s' % (i, s) for i, s in enumerate(lines)]))
    table = (
        # n is the 1-based offset
        (3, 4,  '// before @others // line 1'),
        (2, 8,  'def spam(): // line 2'),
        (3, 9,  '    pass'),
        (1, 11, 'def eggs(): // line 4'),
        (2, 12, '    pass'),
        (5, 14, '// last line: line 6'),
    )
    for i, data in enumerate(table):
        expected_offset, target_offset, expected_line = data
        p, offset, found = c.gotoCommands.find_file_line(i+1, p=target)
        assert expected_offset == offset, (
            'i: %s expected offset %s, got %s' % (i, expected_offset, offset))
        expected_line = expected_line.rstrip()
        got_line = stripped_lines[i].rstrip()
        assert expected_line == got_line, 'i: %s expected line:\n%s\ngot line:\n%s' % (
            i, expected_line, got_line)
        got_line2 = lines[target_offset].rstrip()
        assert expected_line == got_line2, 'i: %s expected line:\n%s\ngot line2:\n%s' % (
            i, expected_line, got_line)
    i = len(table)
    p, offset, found = c.gotoCommands.find_file_line(i+1, p=target)
    assert not found
</t>
<t tx="ekr.20160403143655.1"># Not valid for external tests: uses @&lt;file&gt; node.
if g.app.isExternalUnitTest:
    self.skipTest('Can not be run externally')
else:
    trace = False
    root = p.parent().parent()
    h = '@clean unittest/at-clean-line-number-test.py'
    target = g.findNodeAnywhere(c, h)
    assert target, 'no target'
    s = c.gotoCommands.get_external_file_with_sentinels(target)
    lines = g.splitLines(s)
    stripped_lines = [z for z in lines if not z.startswith('#@')]
    if trace:
        print(''.join(['%3s %s' % (i, s) for i, s in enumerate(lines)]))
        # print('')
        # print(''.join(['%3s %s' % (i, s) for i, s in enumerate(stripped_lines)]))
    table = (
        # n is the 1-based offset
        (3, 4,  '# Before @others: line 1'),
        (2, 8,  'def spam(): # line 2'),
        (3, 9,  '    pass'),
        (1, 11, 'def eggs(): # line 4'),
        (2, 12, '    pass'),
        (5, 14, '# Last line: line 6'),
    )
    for i, data in enumerate(table):
        expected_offset, target_offset, expected_line = data
        p, offset, found = c.gotoCommands.find_file_line(i+1, p=target)
        assert expected_offset == offset, (
            'i: %s expected offset %s, got %s' % (i, expected_offset, offset))
        expected_line = expected_line.rstrip()
        got_line = stripped_lines[i].rstrip()
        assert expected_line == got_line, 'i: %s expected line:\n%s\ngot line:\n%s' % (
            i, expected_line, got_line)
        got_line2 = lines[target_offset].rstrip()
        assert expected_line == got_line2, 'i: %s expected line:\n%s\ngot line2:\n%s' % (
            i, expected_line, got_line)
    i = len(table)
    p, offset, found = c.gotoCommands.find_file_line(i+1, p=target)
    assert not found
</t>
<t tx="ekr.20160403150121.1">def eggs(): // line 4
    pass
</t>
<t tx="ekr.20160403150216.1">def spam(): # line 2
    pass
</t>
<t tx="ekr.20160403150222.1">def eggs(): # line 4
    pass
</t>
<t tx="ekr.20160403152507.1">def eggs(): # line 4
    pass</t>
<t tx="ekr.20160523094102.1"># Great ast docs: http://greentreesnakes.readthedocs.io/en/latest/nodes.html

import leo.core.leoAst as leoAst
leoAst.unit_test(raise_on_fail=True)

import leo.external.make_stub_files as msf
msf.unit_test(raise_on_fail=True)

import leo.external.py2cs as py2cs
py2cs.unit_test(raise_on_fail=True)

</t>
<t tx="ekr.20161011092326.7"># ~/at-auto-test.py

# This is valid Python, but it looks like a section reference.
a = b &lt;&lt; c &gt;&gt; d

</t>
<t tx="ekr.20161011095551.1">True: (Experimental): The @auto write code expands section references.
False: (Legacy):      The @auto write code ignores section references.
</t>
<t tx="ekr.20161103075725.1"># These fail with the NEW (strict) import tests and the OLD scanners.</t>
<t tx="ekr.20161112061406.1">class TypeJoinVisitor(TypeVisitor[Type]):
    """Implementation of the least upper bound algorithm.

    Attributes:
      s: The other (left) type operand.
    """

    def __init__(self, s: Type) -&gt; None:
        self.s = s

    def visit_unbound_type(self, t: UnboundType) -&gt; Type:
        if isinstance(self.s, Void) or isinstance(self.s, ErrorType):
            return ErrorType()
        else:
            return AnyType()

    def visit_union_type(self, t: UnionType) -&gt; Type:
        if is_subtype(self.s, t):
            return t
        else:
            return UnionType(t.items + [self.s])

    def visit_error_type(self, t: ErrorType) -&gt; Type:
        return t

    def visit_type_list(self, t: TypeList) -&gt; Type:
        assert False, 'Not supported'

    def visit_any(self, t: AnyType) -&gt; Type:
        return t

    def visit_void(self, t: Void) -&gt; Type:
        if isinstance(self.s, Void):
            return t
        else:
            return ErrorType()

    def visit_none_type(self, t: NoneTyp) -&gt; Type:
        if not isinstance(self.s, Void):
            return self.s
        else:
            return self.default(self.s)

    def visit_erased_type(self, t: ErasedType) -&gt; Type:
        return self.s

    def visit_type_var(self, t: TypeVarType) -&gt; Type:
        if isinstance(self.s, TypeVarType) and (cast(TypeVarType, self.s)).id == t.id:
            return self.s
        else:
            return self.default(self.s)

    def visit_instance(self, t: Instance) -&gt; Type:
        if isinstance(self.s, Instance):
            return join_instances(t, cast(Instance, self.s))
        elif isinstance(self.s, FunctionLike):
            return join_types(t, self.s.fallback)
        else:
            return self.default(self.s)

    def visit_callable_type(self, t: CallableType) -&gt; Type:
        # TODO: Consider subtyping instead of just similarity.
        if isinstance(self.s, CallableType) and is_similar_callables(
                t, cast(CallableType, self.s)):
            return combine_similar_callables(t, cast(CallableType, self.s))
        elif isinstance(self.s, Overloaded):
            # Switch the order of arguments to that we'll get to visit_overloaded.
            return join_types(t, self.s)
        else:
            return join_types(t.fallback, self.s)

    def visit_overloaded(self, t: Overloaded) -&gt; Type:
        # This is more complex than most other cases. Here are some
        # examples that illustrate how this works.
        #
        # First let's define a concise notation:
        #  - Cn are callable types (for n in 1, 2, ...)
        #  - Ov(C1, C2, ...) is an overloaded type with items C1, C2, ...
        #  - Callable[[T, ...], S] is written as [T, ...] -&gt; S.
        #
        # We want some basic properties to hold (assume Cn are all
        # unrelated via Any-similarity):
        #
        #   join(Ov(C1, C2), C1) == C1
        #   join(Ov(C1, C2), Ov(C1, C2)) == Ov(C1, C2)
        #   join(Ov(C1, C2), Ov(C1, C3)) == C1
        #   join(Ov(C2, C2), C3) == join of fallback types
        #
        # The presence of Any types makes things more interesting. The join is the
        # most general type we can get with respect to Any:
        #
        #   join(Ov([int] -&gt; int, [str] -&gt; str), [Any] -&gt; str) == Any -&gt; str
        #
        # We could use a simplification step that removes redundancies, but that's not
        # implemented right now. Consider this example, where we get a redundancy:
        #
        #   join(Ov([int, Any] -&gt; Any, [str, Any] -&gt; Any), [Any, int] -&gt; Any) ==
        #       Ov([Any, int] -&gt; Any, [Any, int] -&gt; Any)
        #
        # TODO: Use callable subtyping instead of just similarity.
        result = []  # type: List[CallableType]
        s = self.s
        if isinstance(s, FunctionLike):
            # The interesting case where both types are function types.
            for t_item in t.items():
                for s_item in s.items():
                    if is_similar_callables(t_item, s_item):
                        result.append(combine_similar_callables(t_item, s_item))
            if result:
                # TODO: Simplify redundancies from the result.
                if len(result) == 1:
                    return result[0]
                else:
                    return Overloaded(result)
            return join_types(t.fallback, s.fallback)
        return join_types(t.fallback, s)

    def visit_tuple_type(self, t: TupleType) -&gt; Type:
        if (isinstance(self.s, TupleType) and
                cast(TupleType, self.s).length() == t.length()):
            items = []  # type: List[Type]
            for i in range(t.length()):
                items.append(self.join(t.items[i],
                                       (cast(TupleType, self.s)).items[i]))
            # TODO: What if the fallback types are different?
            return TupleType(items, t.fallback)
        else:
            return self.default(self.s)

    def join(self, s: Type, t: Type) -&gt; Type:
        return join_types(s, t)

    def default(self, typ: Type) -&gt; Type:
        if isinstance(typ, Instance):
            return object_from_instance(typ)
        elif isinstance(typ, UnboundType):
            return AnyType()
        elif isinstance(typ, Void) or isinstance(typ, ErrorType):
            return ErrorType()
        elif isinstance(typ, TupleType):
            return self.default(typ.fallback)
        elif isinstance(typ, FunctionLike):
            return self.default(typ.fallback)
        elif isinstance(typ, TypeVarType):
            return self.default(typ.upper_bound)
        else:
            return AnyType()
</t>
<t tx="ekr.20161112061414.1">class aClass:
    @others
</t>
<t tx="ekr.20161112061414.2">def __init__(self):
    pass
</t>
<t tx="ekr.20161112061414.3">def spam(self):
    pass
</t>
<t tx="ekr.20161112061414.4">bClass = aClass
</t>
<t tx="ekr.20161123080832.1">'''
Create a table of expected headlines in a unit test.

Usage: select the desired subnode of an @test node.
'''
g.cls()
# Proper escapes are tricky.
if p.parent() and p.parent().h.startswith('@test'):
    table = [
        '(%s, "%s"),' % (
            p.level()-c.p.level(),
            p.h.replace('\\', '\\\\').replace('"', '\\"'),
        )
            for p in p.subtree()
    ]
    print("table = (\n    %s\n)" % '\n    '.join(table))
else:
    print('select a child of an @test node node')</t>
<t tx="ekr.20161129030232.1"></t>
<t tx="ekr.20161129104243.1">import leo.core.leoImport as leoImport
if leoImport.docutils is None:
    self.skipTest('no docutils')

if 0:
    # The preamble...
    # g.cls()
    if c.isChanged(): c.save()
    import leo
    import leo.core.leoImport as leoImport
    import leo.plugins.importers.linescanner as linescanner
    import leo.plugins.importers.leo_rst as leo_rst
    # Reload all.
    import imp
    imp.reload(leo.plugins.importers.linescanner)
    imp.reload(leo.plugins.importers.leo_rst)
    imp.reload(leoImport)
    g.app.loadManager.createAllImporetersData()
    ic = leoImport.LeoImportCommands(c)
else:
    ic = c.importCommands

# Notes:
# All heading must be followed by an empty line.
### g.app.suppressImportChecks = True
s = '''\
  #########
Chapter 1
  #########

It was a dark and stormy night.
section 1
+++++++++

Sec 1.
section 2
+++++++++

Sec 2.
'''
table = (
    '!Dummy chapter',
    'section 1',
    'section 2',
)
try:
    ic.rstUnitTest(p,s=s,showTree=True)
    if 1:
        root = c.p.lastChild()
        assert root.h.startswith('@@'), root.h
        p2 = root.firstChild()
        for h in table:
            assert p2.h == h, (p2.h, h)
            p2.moveToThreadNext()
        assert not root.isAncestorOf(p2), p2.h # Extra nodes
finally:
    if 1:
        p.deleteAllChildren()
        c.redraw()
</t>
<t tx="ekr.20161130041921.1">#section 1
Sec 1.

#section 2
Sec 2.
@language python
@tabwidth -4
@ignore
</t>
<t tx="ekr.20161130051657.1">s = '''\
Leading text in root node of subtree

Etc. etc.

### A level one node ######################################################

This would be the text in this level one node.

And this.

### Another level one node ################################################

Another one

#### A level 2 node #######################################################

See what we did there - one more '#' - this is a subnode.
'''
ic = c.importCommands
ic.ctextUnitTest(p,fileName='@auto-ctext test.txt',s=s,showTree=True)</t>
<t tx="ekr.20161130052935.1">Leading text in root node of subtree

Etc. etc.
</t>
<t tx="ekr.20161130053149.1">Leading text in root node of subtree

Etc. etc.

### A level one node ######################################################

This would be the text in this level one node.

And this.

### Another level one node ################################################

Another one

#### A level 2 node #######################################################

See what we did there - one more '#' - this is a subnode.

</t>
<t tx="ekr.20161204040924.1"># Alt-G works.  The tests themselves are erroneous.
</t>
<t tx="ekr.20161224111342.1">import glob
import os
if 0: # Preamble...
    g.cls()
    if c.isChanged(): c.save()
    # import leo
    import leo.core.leoImport as leoImport
    # import leo.plugins.importers.basescanner as basescanner
    import leo.plugins.importers.linescanner as linescanner
    import leo.plugins.importers.python
    # Reload all.
    import imp
    # imp.reload(leo.plugins.importers.basescanner)
    imp.reload(leo.plugins.importers.linescanner)
    imp.reload(leo.plugins.importers.python)
    imp.reload(leoImport)
    ic = leoImport.LeoImportCommands(c)
else:
    ic = c.importCommands

try:
    base_dir = g.os_path_finalize_join(g.app.loadDir, '..', 'core')
    assert g.os_path_exists(base_dir), base_dir
    files = glob.glob('%s%s%s' % (base_dir, os.sep, '*.py'))
    files = [z for z in files if g.shortFileName(z).startswith('leo')]
    # leoTangle contains section refs in @c sections.
    files = [z for z in files
        if not g.shortFileName(z).endswith('leoTangle.py')]
    if 0: # Do only files in the table.
        table = (
            'leoColorizer.py',
            'leoFileCommands.py',
            'leoNodes.py',
        )
        result = []
        for fn in files:
            for z in table:
                if g.shortFileName(fn).endswith(z):
                    result.append(fn)
        files = result
    test_node = p.copy()
    fails = []
    test_node.deleteAllChildren()
    for fn in files:
        sfn = g.shortFileName(fn)
        s = g.readFileIntoUnicodeString(fn)
        s2 = ic.removeSentinelLines(s, '#', None, None)
        try:
            print(sfn)
            ic.pythonUnitTest(p,s=s2,showTree=True)
            test_node.lastChild().h = '@@file %s' % sfn
        except AssertionError:
            print('FAIL: %6s %6s %s' % (len(s), len(s2), sfn))
            fails.append(sfn)
            # break
    if fails:
        print('%s failures' % (len(fails)))
        g.printList(sorted(fails))
    elif 1:
        test_node.deleteAllChildren()
finally:
    c.redraw()
</t>
<t tx="ekr.20161228071435.1">if isPython3: # g.not defined yet.
    &lt;&lt; u:1 &gt;&gt;
    &lt;&lt; ue:1 &gt;&gt;
else:
    &lt;&lt; u:2 &gt;&gt;
    &lt;&lt; ue: 2&gt;&gt;
</t>
<t tx="ekr.20161228071822.1">def u(s):
    '''Return s, converted to unicode from Qt widgets.'''
    return s</t>
<t tx="ekr.20161228071836.1">def ue(s, encoding):
    return s if g.isUnicode(s) else str(s, encoding)</t>
<t tx="ekr.20161228071849.1">def u(s):
    '''Return s, converted to unicode from Qt widgets.'''
    return builtins.unicode(s) # Suppress pyflakes complaint.</t>
<t tx="ekr.20161228071905.1">def ue(s, encoding):
    return builtins.unicode(s, encoding)</t>
<t tx="ekr.20161228071915.1">@language python
@tabwidth -4
@others
</t>
<t tx="ekr.20161228072352.1">if isPython3: # g.not defined yet.
    def u(s):
        '''Return s, converted to unicode from Qt widgets.'''
        return s
    def ue(s, encoding):
        return s if g.isUnicode(s) else str(s, encoding)
else:
    def u(s):
        '''Return s, converted to unicode from Qt widgets.'''
        return builtins.unicode(s) # Suppress pyflakes complaint.
    def ue(s, encoding):
        return builtins.unicode(s, encoding)
</t>
<t tx="ekr.20170101134417.1"></t>
<t tx="ekr.20170128023431.1">@language html
</t>
<t tx="ekr.20170401122024.7">@language cweb
@ % This file is part of CWEB.
% This program by Silvio Levy and Donald E. Knuth
% is based on a program by Knuth.
% It is distributed WITHOUT ANY WARRANTY, express or implied.
% Version 3.61 --- July 2000
% (essentially the same as version 3.6, which added
%  recently introduced features of standard C++ to version 3.4)

% Copyright (C) 1987,1990,1993,2000 Silvio Levy and Donald E. Knuth

% Permission is granted to make and distribute verbatim copies of this
% document provided that the copyright notice and this permission notice
% are preserved on all copies.

% Permission is granted to copy and distribute modified versions of this
% document under the conditions for verbatim copying, provided that the
% entire resulting derived work is given a different name and distributed
% under the terms of a permission notice identical to this one.

% Here is TeX material that gets inserted after \input cwebmac
\def\hang{\hangindent 3em\indent\ignorespaces}
\def\pb{$\.|\ldots\.|$} % C brackets (|...|)
\def\v{\char'174} % vertical (|) in typewriter font
\def\dleft{[\![} \def\dright{]\!]} % double brackets
\mathchardef\RA="3221 % right arrow
\mathchardef\BA="3224 % double arrow
\def\({} % ) kludge for alphabetizing certain section names
\def\TeXxstring{\\{\TEX/\_string}}
\def\skipxTeX{\\{skip\_\TEX/}}
\def\copyxTeX{\\{copy\_\TEX/}}

\def\title{CWEAVE (Version 3.61)}
\def\topofcontents{\null\vfill
  \centerline{\titlefont The {\ttitlefont CWEAVE} processor}
  \vskip 15pt
  \centerline{(Version 3.61)}
  \vfill}
\def\botofcontents{\vfill
\noindent
Copyright \copyright\ 1987, 1990, 1993, 2000 Silvio Levy and Donald E. Knuth
\bigskip\noindent
Permission is granted to make and distribute verbatim copies of this
document provided that the copyright notice and this permission notice
are preserved on all copies.

\smallskip\noindent
Permission is granted to copy and distribute modified versions of this
document under the conditions for verbatim copying, provided that the
entire resulting derived work is given a different name and distributed
under the terms of a permission notice identical to this one.
}
\pageno=\contentspagenumber \advance\pageno by 1
\let\maybe=\iftrue
@s not_eq normal @q unreserve a C++ keyword @&gt;@** Introduction. This is the \.{CWEAVE} program by Silvio Levy and Donald E. Knuth, based on \.{WEAVE} by Knuth. We are thankful to Steve Avery, Nelson Beebe, Hans-Hermann Bode (to whom the original \CPLUSPLUS/ adaptation is due), Klaus Guntermann, Norman Ramsey, Tomas Rokicki, Joachim Schnitter, Joachim Schrod, Lee Wittenberg, Saroj Mahapatra, Cesar Augusto Rorato Crusius, and others who have contributed improvements.  The ``banner line'' defined here should be changed whenever \.{CWEAVE} is modified.

@d banner "This is CWEAVE (Version 3.61)\n"

@c @&lt;Include files@&gt;@/
@h
@&lt;Common code for \.{CWEAVE} and \.{CTANGLE}@&gt;@/
@&lt;Typedef declarations@&gt;@/
@&lt;Global variables@&gt;@/
@&lt;Predeclaration of procedures@&gt;
@ We predeclare several standard system functions here instead of including their system header files, because the names of the header files are not as standard as the names of the functions. (For example, some \CEE/ environments have \.{&lt;string.h&gt;} where others have \.{&lt;strings.h&gt;}.)

@&lt;Predeclaration of procedures@&gt;=
extern int strlen(); /* length of string */
extern int strcmp(); /* compare strings lexicographically */
extern char* strcpy(); /* copy one string to another */
extern int strncmp(); /* compare up to $n$ string characters */
extern char* strncpy(); /* copy up to $n$ string characters */
@ \.{CWEAVE} has a fairly straightforward outline.  It operates in three phases: First it inputs the source file and stores cross-reference data, then it inputs the source once again and produces the \TEX/ output file, finally it sorts and outputs the index.  Please read the documentation for \.{common}, the set of routines common to \.{CTANGLE} and \.{CWEAVE}, before proceeding further.

@c
int main (ac, av)
int ac; /* argument count */
char **av; /* argument values */
{
  argc=ac; argv=av;
  program=cweave;
  make_xrefs=force_lines=1; /* controlled by command-line options */
  common_init();
  @&lt;Set initial values@&gt;;
  if (show_banner) printf(banner); /* print a ``banner line'' */
  @&lt;Store all the reserved words@&gt;;
  phase_one(); /* read all the user's text and store the cross-references */
  phase_two(); /* read all the text again and translate it to \TEX/ form */
  phase_three(); /* output the cross-reference index */
  return wrap_up(); /* and exit gracefully */
}@ We have to get \CEE/'s reserved words into the hash table, and the simplest way to do this is to insert them every time \.{CWEAVE} is run.  Fortunately there are relatively few reserved words. (Some of these are not strictly ``reserved,'' but are defined in header files of the ISO Standard \CEE/ Library.) @^reserved words@&gt;

@&lt;Store all the reserved words@&gt;=
id_lookup("and",NULL,alfop);
id_lookup("and_eq",NULL,alfop);
id_lookup("asm",NULL,sizeof_like);
id_lookup("auto",NULL,int_like);
id_lookup("bitand",NULL,alfop);
id_lookup("bitor",NULL,alfop);
id_lookup("bool",NULL,raw_int);
id_lookup("break",NULL,case_like);
id_lookup("case",NULL,case_like);
id_lookup("catch",NULL,catch_like);
id_lookup("char",NULL,raw_int);
id_lookup("class",NULL,struct_like);
id_lookup("clock_t",NULL,raw_int);
id_lookup("compl",NULL,alfop);
id_lookup("const",NULL,const_like);
id_lookup("const_cast",NULL,raw_int);
id_lookup("continue",NULL,case_like);
id_lookup("default",NULL,case_like);
id_lookup("define",NULL,define_like);
id_lookup("defined",NULL,sizeof_like);
id_lookup("delete",NULL,delete_like);
id_lookup("div_t",NULL,raw_int);
id_lookup("do",NULL,do_like);
id_lookup("double",NULL,raw_int);
id_lookup("dynamic_cast",NULL,raw_int);
id_lookup("elif",NULL,if_like);
id_lookup("else",NULL,else_like);
id_lookup("endif",NULL,if_like);
id_lookup("enum",NULL,struct_like);
id_lookup("error",NULL,if_like);
id_lookup("explicit",NULL,int_like);
id_lookup("export",NULL,int_like);
id_lookup("extern",NULL,int_like);
id_lookup("FILE",NULL,raw_int);
id_lookup("float",NULL,raw_int);
id_lookup("for",NULL,for_like);
id_lookup("fpos_t",NULL,raw_int);
id_lookup("friend",NULL,int_like);
id_lookup("goto",NULL,case_like);
id_lookup("if",NULL,if_like);
id_lookup("ifdef",NULL,if_like);
id_lookup("ifndef",NULL,if_like);
id_lookup("include",NULL,if_like);
id_lookup("inline",NULL,int_like);
id_lookup("int",NULL,raw_int);
id_lookup("jmp_buf",NULL,raw_int);
id_lookup("ldiv_t",NULL,raw_int);
id_lookup("line",NULL,if_like);
id_lookup("long",NULL,raw_int);
id_lookup("mutable",NULL,int_like);
id_lookup("namespace",NULL,struct_like);
id_lookup("new",NULL,new_like);
id_lookup("not",NULL,alfop);
id_lookup("not_eq",NULL,alfop);
id_lookup("NULL",NULL,custom);
id_lookup("offsetof",NULL,raw_int);
id_lookup("operator",NULL,operator_like);
id_lookup("or",NULL,alfop);
id_lookup("or_eq",NULL,alfop);
id_lookup("pragma",NULL,if_like);
id_lookup("private",NULL,public_like);
id_lookup("protected",NULL,public_like);
id_lookup("ptrdiff_t",NULL,raw_int);
id_lookup("public",NULL,public_like);
id_lookup("register",NULL,int_like);
id_lookup("reinterpret_cast",NULL,raw_int);
id_lookup("return",NULL,case_like);
id_lookup("short",NULL,raw_int);
id_lookup("sig_atomic_t",NULL,raw_int);
id_lookup("signed",NULL,raw_int);
id_lookup("size_t",NULL,raw_int);
id_lookup("sizeof",NULL,sizeof_like);
id_lookup("static",NULL,int_like);
id_lookup("static_cast",NULL,raw_int);
id_lookup("struct",NULL,struct_like);
id_lookup("switch",NULL,for_like);
id_lookup("template",NULL,template_like);
id_lookup("this",NULL,custom);
id_lookup("throw",NULL,case_like);
id_lookup("time_t",NULL,raw_int);
id_lookup("try",NULL,else_like);
id_lookup("typedef",NULL,typedef_like);
id_lookup("typeid",NULL,raw_int);
id_lookup("typename",NULL,struct_like);
id_lookup("undef",NULL,if_like);
id_lookup("union",NULL,struct_like);
id_lookup("unsigned",NULL,raw_int);
id_lookup("using",NULL,int_like);
id_lookup("va_dcl",NULL,decl); /* Berkeley's variable-arg-list convention */
id_lookup("va_list",NULL,raw_int); /* ditto */
id_lookup("virtual",NULL,int_like);
id_lookup("void",NULL,raw_int);
id_lookup("volatile",NULL,const_like);
id_lookup("wchar_t",NULL,raw_int);
id_lookup("while",NULL,for_like);
id_lookup("xor",NULL,alfop);
id_lookup("xor_eq",NULL,alfop);
res_wd_end=name_ptr;
id_lookup("TeX",NULL,custom);
id_lookup("make_pair",NULL,func_template);
@ The following parameters were sufficient in the original \.{WEAVE} to handle \TEX/, so they should be sufficient for most applications of \.{CWEAVE}. If you change |max_bytes|, |max_names|, |hash_size|, or |buf_size| you have to change them also in the file |"common.w"|.

@d max_bytes 90000 /* the number of bytes in identifiers,
  index entries, and section names */
@d max_names 4000 /* number of identifiers, strings, section names;
  must be less than 10240; used in |"common.w"| */
@d max_sections 2000 /* greater than the total number of sections */
@d hash_size 353 /* should be prime */
@d buf_size 100 /* maximum length of input line, plus one */
@d longest_name 10000 /* section names and strings shouldn't be longer than this */
@d long_buf_size (buf_size+longest_name)
@d line_length 80 /* lines of \TEX/ output have at most this many characters;
  should be less than 256 */
@d max_refs 20000 /* number of cross-references; must be less than 65536 */
@d max_toks 20000 /* number of symbols in \CEE/ texts being parsed;
  must be less than 65536 */
@d max_texts 4000 /* number of phrases in \CEE/ texts being parsed;
  must be less than 10240 */
@d max_scraps 2000 /* number of tokens in \CEE/ texts being parsed */
@d stack_size 400 /* number of simultaneous output levels */

@ The next few sections contain stuff from the file |"common.w"| that must
be included in both |"ctangle.w"| and |"cweave.w"|. It appears in
file |"common.h"|, which needs to be updated when |"common.w"| changes.

@i common.h@* Data structures exclusive to {\tt CWEAVE}. As explained in \.{common.w}, the field of a |name_info| structure that contains the |rlink| of a section name is used for a completely different purpose in the case of identifiers.  It is then called the |ilk| of the identifier, and it is used to distinguish between various types of identifiers, as follows:  \yskip\hang |normal| and |func_template| identifiers are part of the \CEE/ program that will  appear in italic type (or in typewriter type if all uppercase).  \yskip\hang |custom| identifiers are part of the \CEE/ program that will be typeset in special ways.  \yskip\hang |roman| identifiers are index entries that appear after \.{@@\^} in the \.{CWEB} file.  \yskip\hang |wildcard| identifiers are index entries that appear after \.{@@:} in the \.{CWEB} file.  \yskip\hang |typewriter| identifiers are index entries that appear after \.{@@.} in the \.{CWEB} file.  \yskip\hang |alfop|, \dots, |template_like| identifiers are \CEE/ or \CPLUSPLUS/ reserved words whose |ilk| explains how they are to be treated when \CEE/ code is being formatted.

@d ilk dummy.Ilk
@d normal 0 /* ordinary identifiers have |normal| ilk */
@d roman 1 /* normal index entries have |roman| ilk */
@d wildcard 2 /* user-formatted index entries have |wildcard| ilk */
@d typewriter 3 /* `typewriter type' entries have |typewriter| ilk */
@d abnormal(a) (a-&gt;ilk&gt;typewriter) /* tells if a name is special */
@d func_template 4 /* identifiers that can be followed by optional template */
@d custom 5 /* identifiers with user-given control sequence */
@d alfop 22 /* alphabetic operators like \&amp;{and} or \&amp;{not\_eq} */
@d else_like 26 /* \&amp;{else} */
@d public_like 40 /* \&amp;{public}, \&amp;{private}, \&amp;{protected} */
@d operator_like 41 /* \&amp;{operator} */
@d new_like 42 /* \&amp;{new} */
@d catch_like 43 /* \&amp;{catch} */
@d for_like 45 /* \&amp;{for}, \&amp;{switch}, \&amp;{while} */
@d do_like 46 /* \&amp;{do} */
@d if_like 47 /* \&amp;{if}, \&amp;{ifdef}, \&amp;{endif}, \&amp;{pragma}, \dots */
@d delete_like 48 /* \&amp;{delete} */
@d raw_ubin 49 /* `\.\&amp;' or `\.*' when looking for \&amp;{const} following */
@d const_like 50 /* \&amp;{const}, \&amp;{volatile} */
@d raw_int 51 /* \&amp;{int}, \&amp;{char}, \dots; also structure and class names  */
@d int_like 52 /* same, when not followed by left parenthesis or \DC\ */
@d case_like 53 /* \&amp;{case}, \&amp;{return}, \&amp;{goto}, \&amp;{break}, \&amp;{continue} */
@d sizeof_like 54 /* \&amp;{sizeof} */
@d struct_like 55 /* \&amp;{struct}, \&amp;{union}, \&amp;{enum}, \&amp;{class} */
@d typedef_like 56 /* \&amp;{typedef} */
@d define_like 57 /* \&amp;{define} */
@d template_like 58 /* \&amp;{template} */
@ We keep track of the current section number in |section_count|, which is the total number of sections that have started.  Sections which have been altered by a change file entry have their |changed_section| flag turned on during the first phase.

@&lt;Global variables@&gt;=
boolean change_exists; /* has any section changed? */

@ The other large memory area in \.{CWEAVE} keeps the cross-reference data. All uses of the name |p| are recorded in a linked list beginning at |p-&gt;xref|, which points into the |xmem| array. The elements of |xmem| are structures consisting of an integer, |num|, and a pointer |xlink| to another element of |xmem|.  If |x=p-&gt;xref| is a pointer into |xmem|, the value of |x-&gt;num| is either a section number where |p| is used, or |cite_flag| plus a section number where |p| is mentioned, or |def_flag| plus a section number where |p| is defined; and |x-&gt;xlink| points to the next such cross-reference for |p|, if any. This list of cross-references is in decreasing order by section number. The next unused slot in |xmem| is |xref_ptr|. The linked list ends at |&amp;xmem[0]|.  The global variable |xref_switch| is set either to |def_flag| or to zero, depending on whether the next cross-reference to an identifier is to be underlined or not in the index. This switch is set to |def_flag| when \.{@@!} or \.{@

@d} is scanned, and it is cleared to zero when
the next identifier or index entry cross-reference has been made.
Similarly, the global variable |section_xref_switch| is either
|def_flag| or |cite_flag| or zero, depending
on whether a section name is being defined, cited or used in \CEE/ text.

@&lt;Typedef declarations@&gt;=
typedef struct xref_info {
  sixteen_bits num; /* section number plus zero or |def_flag| */
  struct xref_info *xlink; /* pointer to the previous cross-reference */
} xref_info;
typedef xref_info *xref_pointer;

@ @&lt;Global...@&gt;=
xref_info xmem[max_refs]; /* contains cross-reference information */
xref_pointer xmem_end = xmem+max_refs-1;
xref_pointer xref_ptr; /* the largest occupied position in |xmem| */
sixteen_bits xref_switch,section_xref_switch; /* either zero or |def_flag| */

@ A section that is used for multi-file output (with the \.{@@(} feature) has a special first cross-reference whose |num| field is |file_flag|.

@d file_flag (3*cite_flag)
@d def_flag (2*cite_flag)
@d cite_flag 10240 /* must be strictly larger than |max_sections| */
@d xref equiv_or_xref

@&lt;Set initial values@&gt;=
xref_ptr=xmem; name_dir-&gt;xref=(char*)xmem; xref_switch=0; section_xref_switch=0;
xmem-&gt;num=0; /* sentinel value */@ A new cross-reference for an identifier is formed by calling |new_xref|, which discards duplicate entries and ignores non-underlined references to one-letter identifiers or \CEE/'s reserved words.  If the user has sent the |no_xref| flag (the \.{-x} option of the command line), it is unnecessary to keep track of cross-references for identifiers. If one were careful, one could probably make more changes around section 100 to avoid a lot of identifier looking up.

@d append_xref(c) if (xref_ptr==xmem_end) overflow("cross-reference");
  else (++xref_ptr)-&gt;num=c;
@d no_xref (flags['x']==0)
@d make_xrefs flags['x'] /* should cross references be output? */
@d is_tiny(p) ((p+1)-&gt;byte_start==(p)-&gt;byte_start+1)
@d unindexed(a) (a&lt;res_wd_end &amp;&amp; a-&gt;ilk&gt;=custom)
      /* tells if uses of a name are to be indexed */

@c
void
new_xref(p)
name_pointer p;
{
  xref_pointer q; /* pointer to previous cross-reference */
  sixteen_bits m, n; /* new and previous cross-reference value */
  if (no_xref) return;
  if ((unindexed(p) || is_tiny(p)) &amp;&amp; xref_switch==0) return;
  m=section_count+xref_switch; xref_switch=0; q=(xref_pointer)p-&gt;xref;
  if (q != xmem) {
    n=q-&gt;num;
    if (n==m || n==m+def_flag) return;
    else if (m==n+def_flag) {
        q-&gt;num=m; return;
    }
  }
  append_xref(m); xref_ptr-&gt;xlink=q; p-&gt;xref=(char*)xref_ptr;
}@ The cross-reference lists for section names are slightly different. Suppose that a section name is defined in sections $m_1$, \dots, $m_k$, cited in sections $n_1$, \dots, $n_l$, and used in sections $p_1$, \dots, $p_j$.  Then its list will contain $m_1+|def_flag|$, \dots, $m_k+|def_flag|$, $n_1+|cite_flag|$, \dots, $n_l+|cite_flag|$, $p_1$, \dots, $p_j$, in this order.  Although this method of storage takes quadratic time with respect to the length of the list, under foreseeable uses of \.{CWEAVE} this inefficiency is insignificant.

@c
void
new_section_xref(p)
name_pointer p;
{
  xref_pointer q,r; /* pointers to previous cross-references */
  q=(xref_pointer)p-&gt;xref; r=xmem;
  if (q&gt;xmem)
        while (q-&gt;num&gt;section_xref_switch) {r=q; q=q-&gt;xlink;}
  if (r-&gt;num==section_count+section_xref_switch)
        return; /* don't duplicate entries */
  append_xref(section_count+section_xref_switch);
  xref_ptr-&gt;xlink=q; section_xref_switch=0;
  if (r==xmem) p-&gt;xref=(char*)xref_ptr;
  else r-&gt;xlink=xref_ptr;
}@ The cross-reference list for a section name may also begin with |file_flag|. Here's how that flag gets put~in.

@c
void
set_file_flag(p)
name_pointer p;
{
  xref_pointer q;
  q=(xref_pointer)p-&gt;xref;
  if (q-&gt;num==file_flag) return;
  append_xref(file_flag);
  xref_ptr-&gt;xlink = q;
  p-&gt;xref = (char *)xref_ptr;
}@ A third large area of memory is used for sixteen-bit `tokens', which appear in short lists similar to the strings of characters in |byte_mem|. Token lists are used to contain the result of \CEE/ code translated into \TEX/ form; further details about them will be explained later. A |text_pointer| variable is an index into |tok_start|.

@&lt;Typedef declarations@&gt;=
typedef sixteen_bits token;
typedef token *token_pointer;
typedef token_pointer *text_pointer;

@ The first position of |tok_mem| that is unoccupied by replacement text is called |tok_ptr|, and the first unused location of |tok_start| is called |text_ptr|. Thus, we usually have |*text_ptr==tok_ptr|.

@&lt;Global variables@&gt;=
token tok_mem[max_toks]; /* tokens */
token_pointer tok_mem_end = tok_mem+max_toks-1; /* end of |tok_mem| */
token_pointer tok_start[max_texts]; /* directory into |tok_mem| */
token_pointer tok_ptr; /* first unused position in |tok_mem| */
text_pointer text_ptr; /* first unused position in |tok_start| */
text_pointer tok_start_end = tok_start+max_texts-1; /* end of |tok_start| */
token_pointer max_tok_ptr; /* largest value of |tok_ptr| */
text_pointer max_text_ptr; /* largest value of |text_ptr| */

@ @&lt;Set init...@&gt;=
tok_ptr=tok_mem+1; text_ptr=tok_start+1; tok_start[0]=tok_mem+1;
tok_start[1]=tok_mem+1;
max_tok_ptr=tok_mem+1; max_text_ptr=tok_start+1;@ Here are the three procedures needed to complete |id_lookup|:

@c
int names_match(p,first,l,t)
name_pointer p; /* points to the proposed match */
char *first; /* position of first character of string */
int l; /* length of identifier */
eight_bits t; /* desired ilk */
{
  if (length(p)!=l) return 0;
  if (p-&gt;ilk!=t &amp;&amp; !(t==normal &amp;&amp; abnormal(p))) return 0;
  return !strncmp(first,p-&gt;byte_start,l);
}

void
init_p(p,t)
name_pointer p;
eight_bits t;
{
  p-&gt;ilk=t; p-&gt;xref=(char*)xmem;
}

void
init_node(p)
name_pointer p;
{
  p-&gt;xref=(char*)xmem;
}@* Lexical scanning. Let us now consider the subroutines that read the \.{CWEB} source file and break it into meaningful units. There are four such procedures: One simply skips to the next `\.{@@\ }' or `\.{@

@*}' that begins a
section; another passes over the \TEX/ text at the beginning of a
section; the third passes over the \TEX/ text in a \CEE/ comment;
and the last, which is the most interesting, gets the next token of
a \CEE/ text.  They all use the pointers |limit| and |loc| into
the line of input currently being studied.@ Control codes in \.{CWEB}, which begin with `\.{@@}', are converted into a numeric code designed to simplify \.{CWEAVE}'s logic; for example, larger numbers are given to the control codes that denote more significant milestones, and the code of |new_section| should be the largest of all. Some of these numeric control codes take the place of |char| control codes that will not otherwise appear in the output of the scanning routines. @^ASCII code dependencies@&gt;

@d ignore 00 /* control code of no interest to \.{CWEAVE} */
@d verbatim 02 /* takes the place of extended ASCII \.{\char2} */
@d begin_short_comment 03 /* \CPLUSPLUS/ short comment */
@d begin_comment '\t' /* tab marks will not appear */
@d underline '\n' /* this code will be intercepted without confusion */
@d noop 0177 /* takes the place of ASCII delete */
@d xref_roman 0203 /* control code for `\.{@@\^}' */
@d xref_wildcard 0204 /* control code for `\.{@@:}' */
@d xref_typewriter 0205 /* control code for `\.{@@.}' */
@d TeX_string 0206 /* control code for `\.{@@t}' */
@f TeX_string TeX
@d ord 0207 /* control code for `\.{@@'}' */
@d join 0210 /* control code for `\.{@@\&amp;}' */
@d thin_space 0211 /* control code for `\.{@@,}' */
@d math_break 0212 /* control code for `\.{@@\v}' */
@d line_break 0213 /* control code for `\.{@@/}' */
@d big_line_break 0214 /* control code for `\.{@@\#}' */
@d no_line_break 0215 /* control code for `\.{@@+}' */
@d pseudo_semi 0216 /* control code for `\.{@@;}' */
@d macro_arg_open 0220 /* control code for `\.{@@[}' */
@d macro_arg_close 0221 /* control code for `\.{@@]}' */
@d trace 0222 /* control code for `\.{@@0}', `\.{@@1}' and `\.{@@2}' */
@d translit_code 0223 /* control code for `\.{@@l}' */
@d output_defs_code 0224 /* control code for `\.{@@h}' */
@d format_code 0225 /* control code for `\.{@@f}' and `\.{@@s}' */
@d definition 0226 /* control code for `\.{@@d}' */
@d begin_C 0227 /* control code for `\.{@@c}' */
@d section_name 0230 /* control code for `\.{@@&lt;}' */
@d new_section 0231 /* control code for `\.{@@\ }' and `\.{@@*}' */

@ Control codes are converted to \.{CWEAVE}'s internal representation by means of the table |ccode|.

@&lt;Global variables@&gt;=
eight_bits ccode[256]; /* meaning of a char following \.{@@} */

@ @&lt;Set ini...@&gt;=
{int c; for (c=0; c&lt;256; c++) ccode[c]=0;}
ccode[' ']=ccode['\t']=ccode['\n']=ccode['\v']=ccode['\r']=ccode['\f']
   =ccode['*']=new_section;
ccode['@@']='@@'; /* `quoted' at sign */
ccode['=']=verbatim;
ccode['d']=ccode['D']=definition;
ccode['f']=ccode['F']=ccode['s']=ccode['S']=format_code;
ccode['c']=ccode['C']=ccode['p']=ccode['P']=begin_C;
ccode['t']=ccode['T']=TeX_string;
ccode['l']=ccode['L']=translit_code;
ccode['q']=ccode['Q']=noop;
ccode['h']=ccode['H']=output_defs_code;
ccode['&amp;']=join; ccode['&lt;']=ccode['(']=section_name;
ccode['!']=underline; ccode['^']=xref_roman;
ccode[':']=xref_wildcard; ccode['.']=xref_typewriter; ccode[',']=thin_space;
ccode['|']=math_break; ccode['/']=line_break; ccode['#']=big_line_break;
ccode['+']=no_line_break; ccode[';']=pseudo_semi;
ccode['[']=macro_arg_open; ccode[']']=macro_arg_close;
ccode['\'']=ord;
@&lt;Special control codes for debugging@&gt;@;

@ Users can write \.{@@2}, \.{@@1}, and \.{@@0} to turn tracing fully on, partly on, and off, respectively.

@&lt;Special control codes for debugging@&gt;=
ccode['0']=ccode['1']=ccode['2']=trace;
@ The |skip_limbo| routine is used on the first pass to skip through portions of the input that are not in any sections, i.e., that precede the first section. After this procedure has been called, the value of |input_has_ended| will tell whether or not a section has actually been found.  There's a complication that we will postpone until later: If the \.{@@s} operation appears in limbo, we want to use it to adjust the default interpretation of identifiers.

@&lt;Predeclaration of procedures@&gt;=
void skip_limbo();

@ @c
void
skip_limbo() {
  while(1) {
    if (loc&gt;limit &amp;&amp; get_line()==0) return;
    *(limit+1)='@@';
    while (*loc!='@@') loc++; /* look for '@@', then skip two chars */
    if (loc++ &lt;=limit) { int c=ccode[(eight_bits)*loc++];
      if (c==new_section) return;
      if (c==noop) skip_restricted();
      else if (c==format_code) @&lt;Process simple format in limbo@&gt;;
    }
  }
}
@ During the definition and \CEE/ parts of a section, cross-references are made for all identifiers except reserved words. However, the right identifier in a format definition is not referenced, and the left identifier is referenced only if it has been explicitly underlined (preceded by \.{@@!}). The \TEX/ code in comments is, of course, ignored, except for \CEE/ portions enclosed in \pb; the text of a section name is skipped entirely, even if it contains \pb\ constructions.  The variables |lhs| and |rhs| point to the respective identifiers involved in a format definition.

@&lt;Global variables@&gt;=
name_pointer lhs, rhs; /* pointers to |byte_start| for format identifiers */
name_pointer res_wd_end; /* pointer to the first nonreserved identifier */

@ A much simpler processing of format definitions occurs when the definition is found in limbo.

@&lt;Process simple format in limbo@&gt;=
{
  if (get_next()!=identifier)
    err_print("! Missing left identifier of @@s");
@.Missing left identifier...@&gt;
  else {
    lhs=id_lookup(id_first,id_loc,normal);
    if (get_next()!=identifier)
      err_print("! Missing right identifier of @@s");
@.Missing right identifier...@&gt;
    else {
      rhs=id_lookup(id_first,id_loc,normal);
      lhs-&gt;ilk=rhs-&gt;ilk;
    }
  }
}
@ The |skip_TeX| routine is used on the first pass to skip through the \TEX/ code at the beginning of a section. It returns the next control code or `\.{\v}' found in the input. A |new_section| is assumed to exist at the very end of the file.

@f skip_TeX TeX

@c
unsigned
skip_TeX() /* skip past pure \TEX/ code */
{
  while (1) {
    if (loc&gt;limit &amp;&amp; get_line()==0) return(new_section);
    *(limit+1)='@@';
    while (*loc!='@@' &amp;&amp; *loc!='|') loc++;
    if (*loc++ =='|') return('|');
    if (loc&lt;=limit) return(ccode[(eight_bits)*(loc++)]);
  }
}@*1 Inputting the next token. As stated above, \.{CWEAVE}'s most interesting lexical scanning routine is the |get_next| function that inputs the next token of \CEE/ input. However, |get_next| is not especially complicated.  The result of |get_next| is either a |char| code for some special character, or it is a special code representing a pair of characters (e.g., `\.{!=}'), or it is the numeric value computed by the |ccode| table, or it is one of the following special codes:  \yskip\hang |identifier|: In this case the global variables |id_first| and |id_loc| will have been set to the beginning and ending-plus-one locations in the buffer, as required by the |id_lookup| routine.  \yskip\hang |string|: The string will have been copied into the array |section_text|; |id_first| and |id_loc| are set as above (now they are pointers into |section_text|).  \yskip\hang |constant|: The constant is copied into |section_text|, with slight modifications; |id_first| and |id_loc| are set.  \yskip\noindent Furthermore, some of the control codes cause |get_next| to take additional actions:  \yskip\hang |xref_roman|, |xref_wildcard|, |xref_typewriter|, |TeX_string|, |verbatim|: The values of |id_first| and |id_loc| will have been set to the beginning and ending-plus-one locations in the buffer.  \yskip\hang |section_name|: In this case the global variable |cur_section| will point to the |byte_start| entry for the section name that has just been scanned. The value of |cur_section_char| will be |'('| if the section name was preceded by \.{@@(} instead of \.{@

@&lt;}.

\yskip\noindent If |get_next| sees `\.{@@!}'
it sets |xref_switch| to |def_flag| and goes on to the next token.

@d constant 0200 /* \CEE/ constant */
@d string 0201 /* \CEE/ string */
@d identifier 0202 /* \CEE/ identifier or reserved word */

@&lt;Global variables@&gt;=
name_pointer cur_section; /* name of section just scanned */
char cur_section_char; /* the character just before that name */@ @&lt;Include...@&gt;=
#include &lt;ctype.h&gt; /* definition of |isalpha|, |isdigit| and so on */
#include &lt;stdlib.h&gt; /* definition of |exit| */
@ As one might expect, |get_next| consists mostly of a big switch that branches to the various special cases that can arise. \CEE/ allows underscores to appear in identifiers, and some \CEE/ compilers even allow the dollar sign.

@d isxalpha(c) ((c)=='_' || (c)=='$')
   /* non-alpha characters allowed in identifier */
@d ishigh(c) ((eight_bits)(c)&gt;0177)
@^high-bit character handling@&gt;

@&lt;Predeclaration of procedures@&gt;=
eight_bits get_next();
@ @c
eight_bits
get_next() /* produces the next input token */
{@+eight_bits c; /* the current character */
  while (1) {
    @&lt;Check if we're at the end of a preprocessor command@&gt;;
    if (loc&gt;limit &amp;&amp; get_line()==0) return(new_section);
    c=*(loc++);
    if (xisdigit(c) || c=='\\' || c=='.') @&lt;Get a constant@&gt;@;
    else if (c=='\'' || c=='"' || (c=='L'&amp;&amp;(*loc=='\'' || *loc=='"'))@|
           || (c=='&lt;' &amp;&amp; sharp_include_line==1))
        @&lt;Get a string@&gt;@;
    else if (xisalpha(c) || isxalpha(c) || ishigh(c))
      @&lt;Get an identifier@&gt;@;
    else if (c=='@@') @&lt;Get control code and possible section name@&gt;@;
    else if (xisspace(c)) continue; /* ignore spaces and tabs */
    if (c=='#' &amp;&amp; loc==buffer+1) @&lt;Raise preprocessor flag@&gt;;
    mistake: @&lt;Compress two-symbol operator@&gt;@;
    return(c);
  }
}
@ Because preprocessor commands do not fit in with the rest of the syntax of \CEE/, we have to deal with them separately.  One solution is to enclose such commands between special markers.  Thus, when a \.\# is seen as the first character of a line, |get_next| returns a special code |left_preproc| and raises a flag |preprocessing|.  We can use the same internal code number for |left_preproc| as we do for |ord|, since |get_next| changes |ord| into a string.

@d left_preproc ord /* begins a preprocessor command */
@d right_preproc 0217 /* ends a preprocessor command */

@&lt;Global variables@&gt;=
boolean preprocessing=0; /* are we scanning a preprocessor command? */

@ @&lt;Raise prep...@&gt;= {
  preprocessing=1;
  @&lt;Check if next token is |include|@&gt;;
  return (left_preproc);
}
@ An additional complication is the freakish use of \.&lt; and \.&gt; to delimit a file name in lines that start with \.{\#include}.  We must treat this file name as a string.

@&lt;Global variables@&gt;=
boolean sharp_include_line=0; /* are we scanning a |#include| line? */

@ @&lt;Check if next token is |include|@&gt;=
while (loc&lt;=buffer_end-7 &amp;&amp; xisspace(*loc)) loc++;
if (loc&lt;=buffer_end-6 &amp;&amp; strncmp(loc,"include",7)==0) sharp_include_line=1;
@ When we get to the end of a preprocessor line, we lower the flag and send a code |right_preproc|, unless the last character was a \.\\.

@&lt;Check if we're at the end of a preprocessor command@&gt;=
  while (loc==limit-1 &amp;&amp; preprocessing &amp;&amp; *loc=='\\')
    if (get_line()==0) return(new_section); /* still in preprocessor mode */
  if (loc&gt;=limit &amp;&amp; preprocessing) {
    preprocessing=sharp_include_line=0;
    return(right_preproc);
  }
@ The following code assigns values to the combinations
\.{++}, \.{--}, \.{-&gt;}, \.{&gt;=}, \.{&lt;=}, \.{==}, \.{&lt;&lt;},
\.{&gt;&gt;}, \.{!=}, \.{\v\v}, and \.{\&amp;\&amp;}, and to the \CPLUSPLUS/ combinations \.{...}, \.{::}, \.{.*} and \.{-&gt;*}. The compound assignment operators (e.g., \.{+=}) are treated as separate tokens.

@d compress(c) if (loc++&lt;=limit) return(c)

@&lt;Compress two-symbol operator@&gt;=
switch(c) {
  case '/': if (*loc=='*') {compress(begin_comment);}
    else if (*loc=='/') compress(begin_short_comment); break;
  case '+': if (*loc=='+') compress(plus_plus); break;
  case '-': if (*loc=='-') {compress(minus_minus);}
    else if (*loc=='&gt;') if (*(loc+1)=='*') {loc++; compress(minus_gt_ast);}
                        else compress(minus_gt); break;
  case '.': if (*loc=='*') {compress(period_ast);}
            else if (*loc=='.' &amp;&amp; *(loc+1)=='.') {
              loc++; compress(dot_dot_dot);
            }
            break;
  case ':': if (*loc==':') compress(colon_colon); break;
  case '=': if (*loc=='=') compress(eq_eq); break;
  case '&gt;': if (*loc=='=') {compress(gt_eq);}
    else if (*loc=='&gt;') compress(gt_gt); break;
  case '&lt;': if (*loc=='=') {compress(lt_eq);}
    else if (*loc=='&lt;') compress(lt_lt); break;
  case '&amp;': if (*loc=='&amp;') compress(and_and); break;
  case '|': if (*loc=='|') compress(or_or); break;
  case '!': if (*loc=='=') compress(not_eq); break;
}
@ @&lt;Get an identifier@&gt;= {
  id_first=--loc;
  while (isalpha(*++loc) || isdigit(*loc) || isxalpha(*loc) || ishigh(*loc));
  id_loc=loc; return(identifier);
}
@ Different conventions are followed by \TEX/ and \CEE/ to express octal and hexadecimal numbers; it is reasonable to stick to each convention within its realm.  Thus the \CEE/ part of a \.{CWEB} file has octals introduced by \.0 and hexadecimals by \.{0x}, but \.{CWEAVE} will print with \TeX/ macros that the user can redefine to fit the context. In order to simplify such macros, we replace some of the characters.  Notice that in this section and the next, |id_first| and |id_loc| are pointers into the array |section_text|, not into |buffer|.

@&lt;Get a constant@&gt;= {
  id_first=id_loc=section_text+1;
  if (*(loc-1)=='\\') {*id_loc++='~';
  while (xisdigit(*loc)) *id_loc++=*loc++;} /* octal constant */
  else if (*(loc-1)=='0') {
    if (*loc=='x' || *loc=='X') {*id_loc++='^'; loc++;
      while (xisxdigit(*loc)) *id_loc++=*loc++;} /* hex constant */
    else if (xisdigit(*loc)) {*id_loc++='~';
      while (xisdigit(*loc)) *id_loc++=*loc++;} /* octal constant */
    else goto dec; /* decimal constant */
  }
  else { /* decimal constant */
    if (*(loc-1)=='.' &amp;&amp; !xisdigit(*loc)) goto mistake; /* not a constant */
    dec: *id_loc++=*(loc-1);
    while (xisdigit(*loc) || *loc=='.') *id_loc++=*loc++;
    if (*loc=='e' || *loc=='E') { /* float constant */
      *id_loc++='_'; loc++;
      if (*loc=='+' || *loc=='-') *id_loc++=*loc++;
      while (xisdigit(*loc)) *id_loc++=*loc++;
    }
  }
  while (*loc=='u' || *loc=='U' || *loc=='l' || *loc=='L'
         || *loc=='f' || *loc=='F') {
    *id_loc++='$'; *id_loc++=toupper(*loc); loc++;
  }
  return(constant);
}
@ \CEE/ strings and character constants, delimited by double and single quotes, respectively, can contain newlines or instances of their own delimiters if they are protected by a backslash.  We follow this convention, but do not allow the string to be longer than |longest_name|.

@&lt;Get a string@&gt;= {
  char delim = c; /* what started the string */
  id_first = section_text+1;
  id_loc = section_text;
  if (delim=='\'' &amp;&amp; *(loc-2)=='@@') {*++id_loc='@@'; *++id_loc='@@';}
  *++id_loc=delim;
  if (delim=='L') { /* wide character constant */
    delim=*loc++; *++id_loc=delim;
  }
  if (delim=='&lt;') delim='&gt;'; /* for file names in |#include| lines */
  while (1) {
    if (loc&gt;=limit) {
      if(*(limit-1)!='\\') {
        err_print("! String didn't end"); loc=limit; break;
@.String didn't end@&gt;
      }
      if(get_line()==0) {
        err_print("! Input ended in middle of string"); loc=buffer; break;
@.Input ended in middle of string@&gt;
      }
    }
    if ((c=*loc++)==delim) {
      if (++id_loc&lt;=section_text_end) *id_loc=c;
      break;
    }
    if (c=='\\') if (loc&gt;=limit) continue;
      else if (++id_loc&lt;=section_text_end) {
        *id_loc = '\\'; c=*loc++;
      }
    if (++id_loc&lt;=section_text_end) *id_loc=c;
  }
  if (id_loc&gt;=section_text_end) {
    printf("\n! String too long: ");
@.String too long@&gt;
    term_write(section_text+1,25);
    printf("..."); mark_error;
  }
  id_loc++;
  return(string);
}
@ After an \.{@@} sign has been scanned, the next character tells us whether there is more work to do.

@&lt;Get control code and possible section name@&gt;= {
  c=*loc++;
  switch(ccode[(eight_bits)c]) {
    case translit_code: err_print("! Use @@l in limbo only"); continue;
@.Use @@l in limbo...@&gt;
    case underline: xref_switch=def_flag; continue;
    case trace: tracing=c-'0'; continue;
    case xref_roman: case xref_wildcard: case xref_typewriter:
    case noop: case TeX_string: c=ccode[c]; skip_restricted(); return(c);
    case section_name:
      @&lt;Scan the section name and make |cur_section| point to it@&gt;;
    case verbatim: @&lt;Scan a verbatim string@&gt;;
    case ord: @&lt;Get a string@&gt;;
    default: return(ccode[(eight_bits)c]);
  }
}
@ The occurrence of a section name sets |xref_switch| to zero, because the section name might (for example) follow \&amp;{int}.

@&lt;Scan the section name and make |cur_section| point to it@&gt;= {
  char *k; /* pointer into |section_text| */
  cur_section_char=*(loc-1);
  @&lt;Put section name into |section_text|@&gt;;
  if (k-section_text&gt;3 &amp;&amp; strncmp(k-2,"...",3)==0)
        cur_section=section_lookup(section_text+1,k-3,1); /* 1 indicates a prefix */
  else cur_section=section_lookup(section_text+1,k,0);
  xref_switch=0; return(section_name);
}
@ At the present point in the program we have |*(loc-1)==verbatim|; we set |id_first| to the beginning of the string itself, and |id_loc| to its ending-plus-one location in the buffer.  We also set |loc| to the position just after the ending delimiter.

@&lt;Scan a verbatim string@&gt;= {
  id_first=loc++; *(limit+1)='@@'; *(limit+2)='&gt;';
  while (*loc!='@@' || *(loc+1)!='&gt;') loc++;
  if (loc&gt;=limit) err_print("! Verbatim string didn't end");
@.Verbatim string didn't end@&gt;
  id_loc=loc; loc+=2;
  return (verbatim);
}
@ Section names are placed into the |section_text| array with consecutive spaces, tabs, and carriage-returns replaced by single spaces. There will be no spaces at the beginning or the end. (We set |section_text[0]=' '| to facilitate this, since the |section_lookup| routine uses |section_text[1]| as the first character of the name.)

@&lt;Set initial values@&gt;=section_text[0]=' ';

@ @&lt;Put section name...@&gt;=
k=section_text;
while (1) {
  if (loc&gt;limit &amp;&amp; get_line()==0) {
    err_print("! Input ended in section name");
@.Input ended in section name@&gt;
    loc=buffer+1; break;
  }
  c=*loc;
  @&lt;If end of name or erroneous control code, |break|@&gt;;
  loc++; if (k&lt;section_text_end) k++;
  if (xisspace(c)) {
    c=' '; if (*(k-1)==' ') k--;
  }
*k=c;
}
if (k&gt;=section_text_end) {
  printf("\n! Section name too long: ");
@.Section name too long@&gt;
  term_write(section_text+1,25);
  printf("..."); mark_harmless;
}
if (*k==' ' &amp;&amp; k&gt;section_text) k--;
@ @&lt;If end of name...@&gt;=
if (c=='@@') {
  c=*(loc+1);
  if (c=='&gt;') {
    loc+=2; break;
  }
  if (ccode[(eight_bits)c]==new_section) {
    err_print("! Section name didn't end"); break;
@.Section name didn't end@&gt;
  }
  if (c!='@@') {
    err_print("! Control codes are forbidden in section name"); break;
@.Control codes are forbidden...@&gt;
  }
  *(++k)='@@'; loc++; /* now |c==*loc| again */
}
@ This function skips over a restricted context at relatively high speed.

@&lt;Predeclaration of procedures@&gt;=
void skip_restricted();

@ @c
void
skip_restricted()
{
  id_first=loc; *(limit+1)='@@';
false_alarm:
  while (*loc!='@@') loc++;
  id_loc=loc;
  if (loc++&gt;limit) {
    err_print("! Control text didn't end"); loc=limit;
@.Control text didn't end@&gt;
  }
  else {
    if (*loc=='@@'&amp;&amp;loc&lt;=limit) {loc++; goto false_alarm;}
    if (*loc++!='&gt;')
      err_print("! Control codes are forbidden in control text");
@.Control codes are forbidden...@&gt;
  }
}
@** Phase one processing. We now have accumulated enough subroutines to make it possible to carry out \.{CWEAVE}'s first pass over the source file. If everything works right, both phase one and phase two of \.{CWEAVE} will assign the same numbers to sections, and these numbers will agree with what \.{CTANGLE} does.  The global variable |next_control| often contains the most recent output of |get_next|; in interesting cases, this will be the control code that ended a section or part of a section.

@&lt;Global variables@&gt;=
eight_bits next_control; /* control code waiting to be acting upon */@ The overall processing strategy in phase one has the following straightforward outline.

@&lt;Predeclaration of procedures@&gt;=
void phase_one();

@ @c
void
phase_one() {
  phase=1; reset_input(); section_count=0;
  skip_limbo(); change_exists=0;
  while (!input_has_ended)
    @&lt;Store cross-reference data for the current section@&gt;;
  changed_section[section_count]=change_exists;
    /* the index changes if anything does */
  phase=2; /* prepare for second phase */
  @&lt;Print error messages about unused or undefined section names@&gt;;
}
@ @&lt;Store cross-reference data...@&gt;=
{
  if (++section_count==max_sections) overflow("section number");
  changed_section[section_count]=changing;
     /* it will become 1 if any line changes */
  if (*(loc-1)=='*' &amp;&amp; show_progress) {
    printf("*%d",section_count);
    update_terminal; /* print a progress report */
  }
  @&lt;Store cross-references in the \TEX/ part of a section@&gt;;
  @&lt;Store cross-references in the definition part of a section@&gt;;
  @&lt;Store cross-references in the \CEE/ part of a section@&gt;;
  if (changed_section[section_count]) change_exists=1;
}
@ In the \TEX/ part of a section, cross-reference entries are made only for the identifiers in \CEE/ texts enclosed in \pb, or for control texts enclosed in \.{@@\^}$\,\ldots\,$\.{@@&gt;} or \.{@@.}$\,\ldots\,$\.{@@&gt;} or \.{@@:}$\,\ldots\,$\.{@@&gt;}.

@&lt;Store cross-references in the \TEX/ part of a section@&gt;=
while (1) {
  switch (next_control=skip_TeX()) {
    case translit_code: err_print("! Use @@l in limbo only"); continue;
@.Use @@l in limbo...@&gt;
    case underline: xref_switch=def_flag; continue;
    case trace: tracing=*(loc-1)-'0'; continue;
    case '|': C_xref(section_name); break;
    case xref_roman: case xref_wildcard: case xref_typewriter:
    case noop: case section_name:
      loc-=2; next_control=get_next(); /* scan to \.{@@&gt;} */
      if (next_control&gt;=xref_roman &amp;&amp; next_control&lt;=xref_typewriter) {
        @&lt;Replace |"@@@@"| by |"@@"| @&gt;@;
        new_xref(id_lookup(id_first, id_loc,next_control-identifier));
      }
      break;
  }
  if (next_control&gt;=format_code) break;
}
@ @&lt;Replace |"@@@@"| by |"@@"| @&gt;=
{
  char *src=id_first,*dst=id_first;
  while(src&lt;id_loc){
    if(*src=='@@') src++;
    *dst++=*src++;
  }
  id_loc=dst;
  while (dst&lt;src) *dst++=' '; /* clean up in case of error message display */
}
@ When we get to the following code we have |next_control&gt;=format_code|.

@&lt;Store cross-references in the definition part of a section@&gt;=
while (next_control&lt;=definition) { /* |format_code| or |definition| */
  if (next_control==definition) {
    xref_switch=def_flag; /* implied \.{@@!} */
    next_control=get_next();
  } else @&lt;Process a format definition@&gt;;
  outer_xref();
}
@ Error messages for improper format definitions will be issued in phase two. Our job in phase one is to define the |ilk| of a properly formatted identifier, and to remove cross-references to identifiers that we now discover should be unindexed.

@&lt;Process a format definition@&gt;= {
  next_control=get_next();
  if (next_control==identifier) {
    lhs=id_lookup(id_first, id_loc,normal); lhs-&gt;ilk=normal;
    if (xref_switch) new_xref(lhs);
    next_control=get_next();
    if (next_control==identifier) {
      rhs=id_lookup(id_first, id_loc,normal);
      lhs-&gt;ilk=rhs-&gt;ilk;
      if (unindexed(lhs)) { /* retain only underlined entries */
        xref_pointer q,r=NULL;
        for (q=(xref_pointer)lhs-&gt;xref;q&gt;xmem;q=q-&gt;xlink)
          if (q-&gt;num&lt;def_flag)
            if (r) r-&gt;xlink=q-&gt;xlink;
            else lhs-&gt;xref=(char*)q-&gt;xlink;
          else r=q;
      }
      next_control=get_next();
    }
  }
}
@ Finally, when the \TEX/ and definition parts have been treated, we have |next_control&gt;=begin_C|.

@&lt;Store cross-references in the \CEE/ part of a section@&gt;=
if (next_control&lt;=section_name) {  /* |begin_C| or |section_name| */
  if (next_control==begin_C) section_xref_switch=0;
  else {
    section_xref_switch=def_flag;
    if(cur_section_char=='(' &amp;&amp; cur_section!=name_dir)
      set_file_flag(cur_section);
  }
  do {
    if (next_control==section_name &amp;&amp; cur_section!=name_dir)
      new_section_xref(cur_section);
    next_control=get_next(); outer_xref();
  } while ( next_control&lt;=section_name);
}
@ @&lt;Print error messages about un...@&gt;=section_check(root)
@ The |C_xref| subroutine stores references to identifiers in \CEE/ text material beginning with the current value of |next_control| and continuing until |next_control| is `\.\{' or `\.{\v}', or until the next ``milestone'' is passed (i.e., |next_control&gt;=format_code|). If |next_control&gt;=format_code| when |C_xref| is called, nothing will happen; but if |next_control=='|'| upon entry, the procedure assumes that this is the `\.{\v}' preceding \CEE/ text that is to be processed.  The parameter |spec_ctrl| is used to change this behavior. In most cases |C_xref| is called with |spec_ctrl==ignore|, which triggers the default processing described above. If |spec_ctrl==section_name|, section names will be gobbled. This is used when \CEE/ text in the \TEX/ part or inside comments is parsed: It allows for section names to appear in \pb, but these strings will not be entered into the cross reference lists since they are not definitions of section names.  The program uses the fact that our internal code numbers satisfy the relations |xref_roman==identifier+roman| and |xref_wildcard==identifier +wildcard| and |xref_typewriter==identifier+typewriter|, as well as |normal==0|.

@&lt;Predeclaration of procedures@&gt;=
void C_xref();

@ @c
void
C_xref( spec_ctrl ) /* makes cross-references for \CEE/ identifiers */
  eight_bits spec_ctrl;
{
  name_pointer p; /* a referenced name */
  while (next_control&lt;format_code || next_control==spec_ctrl) {
    if (next_control&gt;=identifier &amp;&amp; next_control&lt;=xref_typewriter) {
      if (next_control&gt;identifier) @&lt;Replace |"@@@@"| by |"@@"| @&gt;@;
      p=id_lookup(id_first, id_loc,next_control-identifier); new_xref(p);
    }
    if (next_control==section_name) {
      section_xref_switch=cite_flag;
      new_section_xref(cur_section);
    }
    next_control=get_next();
    if (next_control=='|' || next_control==begin_comment ||
        next_control==begin_short_comment) return;
  }
}
@ The |outer_xref| subroutine is like |C_xref| except that it begins with |next_control!='|'| and ends with |next_control&gt;=format_code|. Thus, it handles \CEE/ text with embedded comments.

@&lt;Predeclaration of procedures@&gt;=
void outer_xref();

@ @c
void
outer_xref() /* extension of |C_xref| */
{
  int bal; /* brace level in comment */
  while (next_control&lt;format_code)
    if (next_control!=begin_comment &amp;&amp; next_control!=begin_short_comment)
      C_xref(ignore);
    else {
      boolean is_long_comment=(next_control==begin_comment);
      bal=copy_comment(is_long_comment,1); next_control='|';
      while (bal&gt;0) {
        C_xref(section_name); /* do not reference section names in comments */
        if (next_control=='|') bal=copy_comment(is_long_comment,bal);
        else bal=0; /* an error message will occur in phase two */
      }
    }
}
@ After phase one has looked at everything, we want to check that each section name was both defined and used.  The variable |cur_xref| will point to cross-references for the current section name of interest.

@&lt;Global variables@&gt;=
xref_pointer cur_xref; /* temporary cross-reference pointer */
boolean an_output; /* did |file_flag| precede |cur_xref|? */

@ The following recursive procedure walks through the tree of section names and prints out anomalies. @^recursion@&gt;

@&lt;Predeclaration of procedures@&gt;=
void section_check();

@ @c
void
section_check(p)
name_pointer p; /* print anomalies in subtree |p| */
{
  if (p) {
    section_check(p-&gt;llink);
    cur_xref=(xref_pointer)p-&gt;xref;
    if (cur_xref-&gt;num==file_flag) {an_output=1; cur_xref=cur_xref-&gt;xlink;}
    else an_output=0;
    if (cur_xref-&gt;num &lt;def_flag) {
      printf("\n! Never defined: &lt;"); print_section_name(p); putchar('&gt;'); mark_harmless;
@.Never defined: &lt;section name&gt;@&gt;
    }
    while (cur_xref-&gt;num &gt;=cite_flag) cur_xref=cur_xref-&gt;xlink;
    if (cur_xref==xmem &amp;&amp; !an_output) {
      printf("\n! Never used: &lt;"); print_section_name(p); putchar('&gt;'); mark_harmless;
@.Never used: &lt;section name&gt;@&gt;
    }
    section_check(p-&gt;rlink);
  }
}
@* Low-level output routines. The \TEX/ output is supposed to appear in lines at most |line_length| characters long, so we place it into an output buffer. During the output process, |out_line| will hold the current line number of the line about to be output.

@&lt;Global variables@&gt;=
char out_buf[line_length+1]; /* assembled characters */
char *out_ptr; /* just after last character in |out_buf| */
char *out_buf_end = out_buf+line_length; /* end of |out_buf| */
int out_line; /* number of next line to be output */@ The |flush_buffer| routine empties the buffer up to a given breakpoint, and moves any remaining characters to the beginning of the next line. If the |per_cent| parameter is 1 a |'%'| is appended to the line that is being output; in this case the breakpoint |b| should be strictly less than |out_buf_end|. If the |per_cent| parameter is |0|, trailing blanks are suppressed. The characters emptied from the buffer form a new line of output; if the |carryover| parameter is true, a |"%"| in that line will be carried over to the next line (so that \TEX/ will ignore the completion of commented-out text).

@d c_line_write(c) fflush(active_file),fwrite(out_buf+1,sizeof(char),c,active_file)
@d tex_putc(c) putc(c,active_file)
@d tex_new_line putc('\n',active_file)
@d tex_printf(c) fprintf(active_file,c)

@c
void
flush_buffer(b,per_cent,carryover)
char *b;  /* outputs from |out_buf+1| to |b|,where |b&lt;=out_ptr| */
boolean per_cent,carryover;
{
  char *j; j=b; /* pointer into |out_buf| */
  if (! per_cent) /* remove trailing blanks */
    while (j&gt;out_buf &amp;&amp; *j==' ') j--;
  c_line_write(j-out_buf);
  if (per_cent) tex_putc('%');
  tex_new_line; out_line++;
  if (carryover)
    while (j&gt;out_buf)
      if (*j--=='%' &amp;&amp; (j==out_buf || *j!='\\')) {
        *b--='%'; break;
      }
  if (b&lt;out_ptr) strncpy(out_buf+1,b+1,out_ptr-b);
  out_ptr-=b-out_buf;
}@ When we are copying \TEX/ source material, we retain line breaks that occur in the input, except that an empty line is not output when the \TEX/ source line was nonempty. For example, a line of the \TEX/ file that contains only an index cross-reference entry will not be copied. The |finish_line| routine is called just before |get_line| inputs a new line, and just after a line break token has been emitted during the output of translated \CEE/ text.

@c
void
finish_line() /* do this at the end of a line */
{
  char *k; /* pointer into |buffer| */
  if (out_ptr&gt;out_buf) flush_buffer(out_ptr,0,0);
  else {
    for (k=buffer; k&lt;=limit; k++)
      if (!(xisspace(*k))) return;
    flush_buffer(out_buf,0,0);
  }
}@ In particular, the |finish_line| procedure is called near the very beginning of phase two. We initialize the output variables in a slightly tricky way so that the first line of the output file will be `\.{\\input cwebmac}'.

@&lt;Set initial values@&gt;=
out_ptr=out_buf+1; out_line=1; active_file=tex_file;
*out_ptr='c'; tex_printf("\\input cwebma");
@ When we wish to append one character |c| to the output buffer, we write `|out(c)|'; this will cause the buffer to be emptied if it was already full.  If we want to append more than one character at once, we say |out_str(s)|, where |s| is a string containing the characters.  A line break will occur at a space or after a single-nonletter \TEX/ control sequence.

@d out(c) {if (out_ptr&gt;=out_buf_end) break_out(); *(++out_ptr)=c;}

@c
void
out_str(s) /* output characters from |s| to end of string */
char *s;
{
  while (*s) out(*s++);
}@ The |break_out| routine is called just before the output buffer is about to overflow. To make this routine a little faster, we initialize position 0 of the output buffer to `\.\\'; this character isn't really output.

@&lt;Set initial values@&gt;=
out_buf[0]='\\';

@ A long line is broken at a blank space or just before a backslash that isn't preceded by another backslash. In the latter case, a |'%'| is output at the break.

@&lt;Predeclaration of procedures@&gt;=
void break_out();

@ @c
void
break_out() /* finds a way to break the output line */
{
  char *k=out_ptr; /* pointer into |out_buf| */
  while (1) {
    if (k==out_buf) @&lt;Print warning message, break the line, |return|@&gt;;
    if (*k==' ') {
      flush_buffer(k,0,1); return;
    }
    if (*(k--)=='\\' &amp;&amp; *k!='\\') { /* we've decreased |k| */
      flush_buffer(k,1,1); return;
    }
  }
}
@ We get to this section only in the unusual case that the entire output line consists of a string of backslashes followed by a string of nonblank non-backslashes. In such cases it is almost always safe to break the line by putting a |'%'| just before the last character.

@&lt;Print warning message, break the line, |return|@&gt;=
{
  printf("\n! Line had to be broken (output l. %d):\n",out_line);
@.Line had to be broken@&gt;
  term_write(out_buf+1, out_ptr-out_buf-1);
  new_line; mark_harmless;
  flush_buffer(out_ptr-1,1,1); return;
}
@ Here is a macro that outputs a section number in decimal notation. The number to be converted by |out_section| is known to be less than |def_flag|, so it cannot have more than five decimal digits.  If the section is changed, we output `\.{\\*}' just after the number.

@c
void
out_section(n)
sixteen_bits n;
{
  char s[6];
  sprintf(s,"%d",n); out_str(s);
  if(changed_section[n]) out_str ("\\*");
@.\\*@&gt;
}@ The |out_name| procedure is used to output an identifier or index entry, enclosing it in braces.

@c
void
out_name(p,quote_xalpha)
name_pointer p;
boolean quote_xalpha;
{
  char *k, *k_end=(p+1)-&gt;byte_start; /* pointers into |byte_mem| */
  out('{');
  for (k=p-&gt;byte_start; k&lt;k_end; k++) {
    if (isxalpha(*k) &amp;&amp; quote_xalpha) out('\\');
@.\\\$@&gt;
@.\\\_@&gt;
    out(*k);
  }
  out('}');
}@* Routines that copy \TEX/ material. During phase two, we use subroutines |copy_limbo|, |copy_TeX|, and |copy_comment| in place of the analogous |skip_limbo|, |skip_TeX|, and |skip_comment| that were used in phase one. (Well, |copy_comment| was actually written in such a way that it functions as |skip_comment| in phase one.)  The |copy_limbo| routine, for example, takes \TEX/ material that is not part of any section and transcribes it almost verbatim to the output file. The use of `\.{@@}' signs is severely restricted in such material: `\.{@@@@}' pairs are replaced by singletons; `\.{@@l}' and `\.{@@q}' and `\.{@@s}' are interpreted.

@c
void
copy_limbo()
{
  char c;
  while (1) {
    if (loc&gt;limit &amp;&amp; (finish_line(), get_line()==0)) return;
    *(limit+1)='@@';
    while (*loc!='@@') out(*(loc++));
    if (loc++&lt;=limit) {
      c=*loc++;
      if (ccode[(eight_bits)c]==new_section) break;
      switch (ccode[(eight_bits)c]) {
        case translit_code: out_str("\\ATL"); break;
@.\\ATL@&gt;
        case '@@': out('@@'); break;
        case noop: skip_restricted(); break;
        case format_code: if (get_next()==identifier) get_next();
          if (loc&gt;=limit) get_line(); /* avoid blank lines in output */
          break; /* the operands of \.{@@s} are ignored on this pass */
        default: err_print("! Double @@ should be used in limbo");
@.Double @@ should be used...@&gt;
        out('@@');
      }
    }
  }
}@ The |copy_TeX| routine processes the \TEX/ code at the beginning of a
section; for example, the words you are now reading were copied in this
way. It returns the next control code or `\.{\v}' found in the input.
We don't copy spaces or tab marks into the beginning of a line. This
makes the test for empty lines in |finish_line| work.

@ @f copy_TeX TeX
@c
eight_bits
copy_TeX()
{
  char c; /* current character being copied */
  while (1) {
    if (loc&gt;limit &amp;&amp; (finish_line(), get_line()==0)) return(new_section);
    *(limit+1)='@@';
    while ((c=*(loc++))!='|' &amp;&amp; c!='@@') {
      out(c);
      if (out_ptr==out_buf+1 &amp;&amp; (xisspace(c))) out_ptr--;
    }
    if (c=='|') return('|');
    if (loc&lt;=limit) return(ccode[(eight_bits)*(loc++)]);
  }
}@ The |copy_comment| function issues a warning if more braces are opened than closed, and in the case of a more serious error it supplies enough braces to keep \TEX/ from complaining about unbalanced braces. Instead of copying the \TEX/ material into the output buffer, this function copies it into the token memory (in phase two only). The abbreviation |app_tok(t)| is used to append token |t| to the current token list, and it also makes sure that it is possible to append at least one further token without overflow.

@d app_tok(c) {if (tok_ptr+2&gt;tok_mem_end) overflow("token"); *(tok_ptr++)=c;}

@&lt;Predeclaration of procedures@&gt;=
int copy_comment();

@ @c
int copy_comment(is_long_comment,bal) /* copies \TEX/ code in comments */
boolean is_long_comment; /* is this a traditional \CEE/ comment? */
int bal; /* brace balance */
{
  char c; /* current character being copied */
  while (1) {
    if (loc&gt;limit) {
      if (is_long_comment) {
        if (get_line()==0) {
          err_print("! Input ended in mid-comment");
@.Input ended in mid-comment@&gt;
          loc=buffer+1; goto done;
        }
      }
      else {
        if (bal&gt;1) err_print("! Missing } in comment");
@.Missing \} in comment@&gt;
        goto done;
      }
    }
    c=*(loc++);
    if (c=='|') return(bal);
    if (is_long_comment) @&lt;Check for end of comment@&gt;;
    if (phase==2) {
      if (ishigh(c)) app_tok(quoted_char);
      app_tok(c);
    }
    @&lt;Copy special things when |c=='@@', '\\'|@&gt;;
    if (c=='{') bal++;
    else if (c=='}') {
      if(bal&gt;1) bal--;
      else {err_print("! Extra } in comment");
@.Extra \} in comment@&gt;
        if (phase==2) tok_ptr--;
      }
    }
  }
done:@&lt;Clear |bal| and |return|@&gt;;
}
@ @&lt;Check for end of comment@&gt;=
if (c=='*' &amp;&amp; *loc=='/') {
  loc++;
  if (bal&gt;1) err_print("! Missing } in comment");
@.Missing \} in comment@&gt;
  goto done;
}
@ @&lt;Copy special things when |c=='@@'...@&gt;=
if (c=='@@') {
  if (*(loc++)!='@@') {
    err_print("! Illegal use of @@ in comment");
@.Illegal use of @@...@&gt;
    loc-=2; if (phase==2) *(tok_ptr-1)=' '; goto done;
  }
}
else if (c=='\\' &amp;&amp; *loc!='@@')
  if (phase==2) app_tok(*(loc++)) else loc++;
@ We output enough right braces to keep \TEX/ happy.

@&lt;Clear |bal| and |return|@&gt;=
if (phase==2) while (bal-- &gt;0) app_tok('}');
return(0);
@** Parsing.
The most intricate part of \.{CWEAVE} is its mechanism for converting
\CEE/-like code into \TEX/ code, and we might as well plunge into this
aspect of the program now. A ``bottom up'' approach is used to parse the
\CEE/-like material, since \.{CWEAVE} must deal with fragmentary
constructions whose overall ``part of speech'' is not known.

At the lowest level, the input is represented as a sequence of entities
that we shall call {\it scraps}, where each scrap of information consists
of two parts, its {\it category} and its {\it translation}. The category
is essentially a syntactic class, and the translation is a token list that
represents \TEX/ code. Rules of syntax and semantics tell us how to
combine adjacent scraps into larger ones, and if we are lucky an entire
\CEE/ text that starts out as hundreds of small scraps will join
together into one gigantic scrap whose translation is the desired \TEX/
code. If we are unlucky, we will be left with several scraps that don't
combine; their translations will simply be output, one by one.

The combination rules are given as context-sensitive productions that are
applied from left to right. Suppose that we are currently working on the
sequence of scraps $s_1\,s_2\ldots s_n$. We try first to find the longest
production that applies to an initial substring $s_1\,s_2\ldots\,$; but if
no such productions exist, we try to find the longest production
applicable to the next substring $s_2\,s_3\ldots\,$; and if that fails, we
try to match $s_3\,s_4\ldots\,$, etc.

A production applies if the category codes have a given pattern. For
example, one of the productions (see rule~3) is
$$\hbox{|exp| }\left\{\matrix{\hbox{|binop|}\cr\hbox{|ubinop|}}\right\}
\hbox{ |exp| }\RA\hbox{ |exp|}$$
and it means that three consecutive scraps whose respective categories are
|exp|, |binop| (or |ubinop|),
and |exp| are converted to one scrap whose category
is |exp|.  The translations of the original
scraps are simply concatenated.  The case of
$$\hbox{|exp| |comma| |exp| $\RA$ |exp|} \hskip4emE_1C\,\\{opt}9\,E_2$$
(rule 4) is only slightly more complicated:
Here the resulting |exp| translation
consists not only of the three original translations, but also of the
tokens |opt| and 9 between the translations of the
|comma| and the following |exp|.
In the \TEX/ file, this will specify an optional line break after the
comma, with penalty 90.

At each opportunity the longest possible production is applied.  For
example, if the current sequence of scraps is |int_like| |cast|
|lbrace|, rule 31 is applied; but if the sequence is |int_like| |cast|
followed by anything other than |lbrace|, rule 32 takes effect.

Translation rules such as `$E_1C\,\\{opt}9\,E_2$' above use subscripts
to distinguish between translations of scraps whose categories have the
same initial letter; these subscripts are assigned from left to right.@ Here is a list of the category codes that scraps can have. (A few others, like |int_like|, have already been defined; the |cat_name| array contains a complete list.)

@d exp 1 /* denotes an expression, including perhaps a single identifier */
@d unop 2 /* denotes a unary operator */
@d binop 3 /* denotes a binary operator */
@d ubinop 4
  /* denotes an operator that can be unary or binary, depending on context */
@d cast 5 /* denotes a cast */
@d question 6 /* denotes a question mark and possibly the expressions flanking it */
@d lbrace 7 /* denotes a left brace */
@d rbrace 8 /* denotes a right brace */
@d decl_head 9 /* denotes an incomplete declaration */
@d comma 10 /* denotes a comma */
@d lpar 11 /* denotes a left parenthesis or left bracket */
@d rpar 12 /* denotes a right parenthesis or right bracket */
@d prelangle 13 /* denotes `$&lt;$' before we know what it is */
@d prerangle 14 /* denotes `$&gt;$' before we know what it is */
@d langle 15 /* denotes `$&lt;$' when it's used as angle bracket in a template */
@d colcol 18 /* denotes `::' */
@d base 19 /* denotes a colon that introduces a base specifier */
@d decl 20 /* denotes a complete declaration */
@d struct_head 21 /* denotes the beginning of a structure specifier */
@d stmt 23 /* denotes a complete statement */
@d function 24 /* denotes a complete function */
@d fn_decl 25 /* denotes a function declarator */
@d semi 27 /* denotes a semicolon */
@d colon 28 /* denotes a colon */
@d tag 29 /* denotes a statement label */
@d if_head 30 /* denotes the beginning of a compound conditional */
@d else_head 31 /* denotes a prefix for a compound statement */
@d if_clause 32 /* pending \.{if} together with a condition */
@d lproc 35 /* begins a preprocessor command */
@d rproc 36 /* ends a preprocessor command */
@d insert 37 /* a scrap that gets combined with its neighbor */
@d section_scrap 38 /* section name */
@d dead 39 /* scrap that won't combine */
@d ftemplate 59 /* \\{make\_pair} */
@d new_exp 60 /* \&amp;{new} and a following type identifier */
@d begin_arg 61 /* \.{@@[} */
@d end_arg 62 /* \.{@@]} */

@&lt;Global variables@&gt;=
char cat_name[256][12];
eight_bits cat_index;

@ @&lt;Set in...@&gt;=
    for (cat_index=0;cat_index&lt;255;cat_index++)
      strcpy(cat_name[cat_index],"UNKNOWN");
@.UNKNOWN@&gt;
    strcpy(cat_name[exp],"exp");
    strcpy(cat_name[unop],"unop");
    strcpy(cat_name[binop],"binop");
    strcpy(cat_name[ubinop],"ubinop");
    strcpy(cat_name[cast],"cast");
    strcpy(cat_name[question],"?");
    strcpy(cat_name[lbrace],"{"@q}@&gt;);
    strcpy(cat_name[rbrace],@q{@&gt;"}");
    strcpy(cat_name[decl_head],"decl_head");
    strcpy(cat_name[comma],",");
    strcpy(cat_name[lpar],"(");
    strcpy(cat_name[rpar],")");
    strcpy(cat_name[prelangle],"&lt;");
    strcpy(cat_name[prerangle],"&gt;");
    strcpy(cat_name[langle],"\\&lt;");
    strcpy(cat_name[colcol],"::");
    strcpy(cat_name[base],"\\:");
    strcpy(cat_name[decl],"decl");
    strcpy(cat_name[struct_head],"struct_head");
    strcpy(cat_name[alfop],"alfop");
    strcpy(cat_name[stmt],"stmt");
    strcpy(cat_name[function],"function");
    strcpy(cat_name[fn_decl],"fn_decl");
    strcpy(cat_name[else_like],"else_like");
    strcpy(cat_name[semi],";");
    strcpy(cat_name[colon],":");
    strcpy(cat_name[tag],"tag");
    strcpy(cat_name[if_head],"if_head");
    strcpy(cat_name[else_head],"else_head");
    strcpy(cat_name[if_clause],"if()");
    strcpy(cat_name[lproc],"#{"@q}@&gt;);
    strcpy(cat_name[rproc],@q{@&gt;"#}");
    strcpy(cat_name[insert],"insert");
    strcpy(cat_name[section_scrap],"section");
    strcpy(cat_name[dead],"@@d");
    strcpy(cat_name[public_like],"public");
    strcpy(cat_name[operator_like],"operator");
    strcpy(cat_name[new_like],"new");
    strcpy(cat_name[catch_like],"catch");
    strcpy(cat_name[for_like],"for");
    strcpy(cat_name[do_like],"do");
    strcpy(cat_name[if_like],"if");
    strcpy(cat_name[delete_like],"delete");
    strcpy(cat_name[raw_ubin],"ubinop?");
    strcpy(cat_name[const_like],"const");
    strcpy(cat_name[raw_int],"raw");
    strcpy(cat_name[int_like],"int");
    strcpy(cat_name[case_like],"case");
    strcpy(cat_name[sizeof_like],"sizeof");
    strcpy(cat_name[struct_like],"struct");
    strcpy(cat_name[typedef_like],"typedef");
    strcpy(cat_name[define_like],"define");
    strcpy(cat_name[template_like],"template");
    strcpy(cat_name[ftemplate],"ftemplate");
    strcpy(cat_name[new_exp],"new_exp");
    strcpy(cat_name[begin_arg],"@@["@q]@&gt;);
    strcpy(cat_name[end_arg],@q[@&gt;"@@]");
    strcpy(cat_name[0],"zero");
@ This code allows \.{CWEAVE} to display its parsing steps.

@c
void
print_cat(c) /* symbolic printout of a category */
eight_bits c;
{
  printf(cat_name[c]);
}@ The token lists for translated \TEX/ output contain some special control symbols as well as ordinary characters. These control symbols are interpreted by \.{CWEAVE} before they are written to the output file.  \yskip\hang |break_space| denotes an optional line break or an en space;  \yskip\hang |force| denotes a line break;  \yskip\hang |big_force| denotes a line break with additional vertical space;  \yskip\hang |preproc_line| denotes that the line will be printed flush left;  \yskip\hang |opt| denotes an optional line break (with the continuation line indented two ems with respect to the normal starting position)---this code is followed by an integer |n|, and the break will occur with penalty $10n$;  \yskip\hang |backup| denotes a backspace of one em;  \yskip\hang |cancel| obliterates any |break_space|, |opt|, |force|, or |big_force| tokens that immediately precede or follow it and also cancels any |backup| tokens that follow it;  \yskip\hang |indent| causes future lines to be indented one more em;  \yskip\hang |outdent| causes future lines to be indented one less em.  \yskip\noindent All of these tokens are removed from the \TEX/ output that comes from \CEE/ text between \pb\ signs; |break_space| and |force| and |big_force| become single spaces in this mode. The translation of other \CEE/ texts results in \TEX/ control sequences \.{\\1}, \.{\\2}, \.{\\3}, \.{\\4}, \.{\\5}, \.{\\6}, \.{\\7}, \.{\\8} corresponding respectively to |indent|, |outdent|, |opt|, |backup|, |break_space|, |force|, |big_force| and |preproc_line|. However, a sequence of consecutive `\.\ ', |break_space|, |force|, and/or |big_force| tokens is first replaced by a single token (the maximum of the given ones).  The token |math_rel| will be translated into \.{\\MRL\{}, and it will get a matching \.\} later. Other control sequences in the \TEX/ output will be `\.{\\\\\{}$\,\ldots\,$\.\}' surrounding identifiers, `\.{\\\&amp;\{}$\,\ldots\,$\.\}' surrounding reserved words, `\.{\\.\{}$\,\ldots\,$\.\}' surrounding strings, `\.{\\C\{}$\,\ldots\,$\.\}$\,$|force|' surrounding comments, and `\.{\\X$n$:}$\,\ldots\,$\.{\\X}' surrounding section names, where |n| is the section number.

@d math_rel 0206
@d big_cancel 0210 /* like |cancel|, also overrides spaces */
@d cancel 0211 /* overrides |backup|, |break_space|, |force|, |big_force| */
@d indent 0212 /* one more tab (\.{\\1}) */
@d outdent 0213 /* one less tab (\.{\\2}) */
@d opt 0214 /* optional break in mid-statement (\.{\\3}) */
@d backup 0215 /* stick out one unit to the left (\.{\\4}) */
@d break_space 0216 /* optional break between statements (\.{\\5}) */
@d force 0217 /* forced break between statements (\.{\\6}) */
@d big_force 0220 /* forced break with additional space (\.{\\7}) */
@d preproc_line 0221 /* begin line without indentation (\.{\\8}) */
@^high-bit character handling@&gt;

@d quoted_char 0222
        /* introduces a character token in the range |0200|--|0377| */
@d end_translation 0223 /* special sentinel token at end of list */
@d inserted 0224 /* sentinel to mark translations of inserts */
@d qualifier 0225 /* introduces an explicit namespace qualifier */
@ The raw input is converted into scraps according to the following table, which gives category codes followed by the translations. \def\stars {\.{**}}% The symbol `\stars' stands for `\.{\\\&amp;\{{\rm identifier}\}}', i.e., the identifier itself treated as a reserved word. The right-hand column is the so-called |mathness|, which is explained further below.  An identifier |c| of length 1 is translated as \.{\\\v c} instead of as \.{\\\\\{c\}}. An identifier \.{CAPS} in all caps is translated as \.{\\.\{CAPS\}} instead of as \.{\\\\\{CAPS\}}. An identifier that has become a reserved word via |typedef| is translated with \.{\\\&amp;} replacing \.{\\\\} and |raw_int| replacing |exp|.  A string of length greater than 20 is broken into pieces of size at most~20 with discretionary breaks in between.  \yskip\halign{\quad#\hfil&amp;\quad#\hfil&amp;\quad\hfil#\hfil\cr \.{!=}&amp;|binop|: \.{\\I}&amp;yes\cr \.{&lt;=}&amp;|binop|: \.{\\Z}&amp;yes\cr \.{&gt;=}&amp;|binop|: \.{\\G}&amp;yes\cr \.{==}&amp;|binop|: \.{\\E}&amp;yes\cr \.{\&amp;\&amp;}&amp;|binop|: \.{\\W}&amp;yes\cr \.{\v\v}&amp;|binop|: \.{\\V}&amp;yes\cr \.{++}&amp;|unop|: \.{\\PP}&amp;yes\cr \.{--}&amp;|unop|: \.{\\MM}&amp;yes\cr \.{-&gt;}&amp;|binop|: \.{\\MG}&amp;yes\cr \.{&gt;&gt;}&amp;|binop|: \.{\\GG}&amp;yes\cr \.{&lt;&lt;}&amp;|binop|: \.{\\LL}&amp;yes\cr \.{::}&amp;|colcol|: \.{\\DC}&amp;maybe\cr \.{.*}&amp;|binop|: \.{\\PA}&amp;yes\cr \.{-&gt;*}&amp;|binop|: \.{\\MGA}&amp;yes\cr \.{...}&amp;|raw_int|: \.{\\,\\ldots\\,}&amp;yes\cr \."string\."&amp;|exp|: \.{\\.\{}string with special characters quoted\.\}&amp;maybe\cr \.{@@=}string\.{@@&gt;}&amp;|exp|: \.{\\vb\{}string with special characters   quoted\.\}&amp;maybe\cr \.{@@'7'}&amp;|exp|: \.{\\.\{@@'7'\}}&amp;maybe\cr \.{077} or \.{\\77}&amp;|exp|: \.{\\T\{\\\~77\}}&amp;maybe\cr \.{0x7f}&amp;|exp|: \.{\\T\{\\\^7f\}}&amp;maybe\cr \.{77}&amp;|exp|: \.{\\T\{77\}}&amp;maybe\cr \.{77L}&amp;|exp|: \.{\\T\{77\\\$L\}}&amp;maybe\cr \.{0.1E5}&amp;|exp|: \.{\\T\{0.1\\\_5\}}&amp;maybe\cr \.+&amp;|ubinop|: \.+&amp;yes\cr \.-&amp;|ubinop|: \.-&amp;yes\cr \.*&amp;|raw_ubin|: \.*&amp;yes\cr \./&amp;|binop|: \./&amp;yes\cr \.&lt;&amp;|prelangle|: \.{\\langle}&amp;yes\cr \.=&amp;|binop|: \.{\\K}&amp;yes\cr \.&gt;&amp;|prerangle|: \.{\\rangle}&amp;yes\cr \..&amp;|binop|: \..&amp;yes\cr \.{\v}&amp;|binop|: \.{\\OR}&amp;yes\cr \.\^&amp;|binop|: \.{\\XOR}&amp;yes\cr \.\%&amp;|binop|: \.{\\MOD}&amp;yes\cr \.?&amp;|question|: \.{\\?}&amp;yes\cr \.!&amp;|unop|: \.{\\R}&amp;yes\cr \.\~&amp;|unop|: \.{\\CM}&amp;yes\cr \.\&amp;&amp;|raw_ubin|: \.{\\AND}&amp;yes\cr \.(&amp;|lpar|: \.(&amp;maybe\cr \.[&amp;|lpar|: \.[&amp;maybe\cr \.)&amp;|rpar|: \.)&amp;maybe\cr \.]&amp;|rpar|: \.]&amp;maybe\cr \.\{&amp;|lbrace|: \.\{&amp;yes\cr \.\}&amp;|lbrace|: \.\}&amp;yes\cr \.,&amp;|comma|: \.,&amp;yes\cr \.;&amp;|semi|: \.;&amp;maybe\cr \.:&amp;|colon|: \.:&amp;no\cr \.\# (within line)&amp;|ubinop|: \.{\\\#}&amp;yes\cr \.\# (at beginning)&amp;|lproc|:  |force| |preproc_line| \.{\\\#}&amp;no\cr end of \.\# line&amp;|rproc|:  |force|&amp;no\cr identifier&amp;|exp|: \.{\\\\\{}identifier with underlines and              dollar signs quoted\.\}&amp;maybe\cr \.{and}&amp;|alfop|: \stars&amp;yes\cr \.{and\_eq}&amp;|alfop|: \stars&amp;yes\cr \.{asm}&amp;|sizeof_like|: \stars&amp;maybe\cr \.{auto}&amp;|int_like|: \stars&amp;maybe\cr \.{bitand}&amp;|alfop|: \stars&amp;yes\cr \.{bitor}&amp;|alfop|: \stars&amp;yes\cr \.{bool}&amp;|raw_int|: \stars&amp;maybe\cr \.{break}&amp;|case_like|: \stars&amp;maybe\cr \.{case}&amp;|case_like|: \stars&amp;maybe\cr \.{catch}&amp;|catch_like|: \stars&amp;maybe\cr \.{char}&amp;|raw_int|: \stars&amp;maybe\cr \.{class}&amp;|struct_like|: \stars&amp;maybe\cr \.{clock\_t}&amp;|raw_int|: \stars&amp;maybe\cr \.{compl}&amp;|alfop|: \stars&amp;yes\cr \.{const}&amp;|const_like|: \stars&amp;maybe\cr \.{const\_cast}&amp;|raw_int|: \stars&amp;maybe\cr \.{continue}&amp;|case_like|: \stars&amp;maybe\cr \.{default}&amp;|case_like|: \stars&amp;maybe\cr \.{define}&amp;|define_like|: \stars&amp;maybe\cr \.{defined}&amp;|sizeof_like|: \stars&amp;maybe\cr \.{delete}&amp;|delete_like|: \stars&amp;maybe\cr \.{div\_t}&amp;|raw_int|: \stars&amp;maybe\cr \.{do}&amp;|do_like|: \stars&amp;maybe\cr \.{double}&amp;|raw_int|: \stars&amp;maybe\cr \.{dynamic\_cast}&amp;|raw_int|: \stars&amp;maybe\cr \.{elif}&amp;|if_like|: \stars&amp;maybe\cr \.{else}&amp;|else_like|: \stars&amp;maybe\cr \.{endif}&amp;|if_like|: \stars&amp;maybe\cr \.{enum}&amp;|struct_like|: \stars&amp;maybe\cr \.{error}&amp;|if_like|: \stars&amp;maybe\cr \.{explicit}&amp;|int_like|: \stars&amp;maybe\cr \.{export}&amp;|int_like|: \stars&amp;maybe\cr \.{extern}&amp;|int_like|: \stars&amp;maybe\cr \.{FILE}&amp;|raw_int|: \stars&amp;maybe\cr \.{float}&amp;|raw_int|: \stars&amp;maybe\cr \.{for}&amp;|for_like|: \stars&amp;maybe\cr \.{fpos\_t}&amp;|raw_int|: \stars&amp;maybe\cr \.{friend}&amp;|int_like|: \stars&amp;maybe\cr \.{goto}&amp;|case_like|: \stars&amp;maybe\cr \.{if}&amp;|if_like|: \stars&amp;maybe\cr \.{ifdef}&amp;|if_like|: \stars&amp;maybe\cr \.{ifndef}&amp;|if_like|: \stars&amp;maybe\cr \.{include}&amp;|if_like|: \stars&amp;maybe\cr \.{inline}&amp;|int_like|: \stars&amp;maybe\cr \.{int}&amp;|raw_int|: \stars&amp;maybe\cr \.{jmp\_buf}&amp;|raw_int|: \stars&amp;maybe\cr \.{ldiv\_t}&amp;|raw_int|: \stars&amp;maybe\cr \.{line}&amp;|if_like|: \stars&amp;maybe\cr \.{long}&amp;|raw_int|: \stars&amp;maybe\cr \.{make\_pair}&amp;|ftemplate|: \.{\\\\\{make\\\_pair\}}&amp;maybe\cr \.{mutable}&amp;|int_like|: \stars&amp;maybe\cr \.{namespace}&amp;|struct_like|: \stars&amp;maybe\cr \.{new}&amp;|new_like|: \stars&amp;maybe\cr \.{not}&amp;|alfop|: \stars&amp;yes\cr \.{not\_eq}&amp;|alfop|: \stars&amp;yes\cr \.{NULL}&amp;|exp|: \.{\\NULL}&amp;yes\cr \.{offsetof}&amp;|raw_int|: \stars&amp;maybe\cr \.{operator}&amp;|operator_like|: \stars&amp;maybe\cr \.{or}&amp;|alfop|: \stars&amp;yes\cr \.{or\_eq}&amp;|alfop|: \stars&amp;yes\cr \.{pragma}&amp;|if_like|: \stars&amp;maybe\cr \.{private}&amp;|public_like|: \stars&amp;maybe\cr \.{protected}&amp;|public_like|: \stars&amp;maybe\cr \.{ptrdiff\_t}&amp;|raw_int|: \stars&amp;maybe\cr \.{public}&amp;|public_like|: \stars&amp;maybe\cr \.{register}&amp;|int_like|: \stars&amp;maybe\cr \.{reinterpret\_cast}&amp;|raw_int|: \stars&amp;maybe\cr \.{return}&amp;|case_like|: \stars&amp;maybe\cr \.{short}&amp;|raw_int|: \stars&amp;maybe\cr \.{sig\_atomic\_t}&amp;|raw_int|: \stars&amp;maybe\cr \.{signed}&amp;|raw_int|: \stars&amp;maybe\cr \.{size\_t}&amp;|raw_int|: \stars&amp;maybe\cr \.{sizeof}&amp;|sizeof_like|: \stars&amp;maybe\cr \.{static}&amp;|int_like|: \stars&amp;maybe\cr \.{static\_cast}&amp;|raw_int|: \stars&amp;maybe\cr \.{struct}&amp;|struct_like|: \stars&amp;maybe\cr \.{switch}&amp;|for_like|: \stars&amp;maybe\cr \.{template}&amp;|template_like|: \stars&amp;maybe\cr \.{TeX}&amp;|exp|: \.{\\TeX}&amp;yes\cr \.{this}&amp;|exp|: \.{\\this}&amp;yes\cr \.{throw}&amp;|case_like|: \stars&amp;maybe\cr \.{time\_t}&amp;|raw_int|: \stars&amp;maybe\cr \.{try}&amp;|else_like|: \stars&amp;maybe\cr \.{typedef}&amp;|typedef_like|: \stars&amp;maybe\cr \.{typeid}&amp;|raw_int|: \stars&amp;maybe\cr \.{typename}&amp;|struct_like|: \stars&amp;maybe\cr \.{undef}&amp;|if_like|: \stars&amp;maybe\cr \.{union}&amp;|struct_like|: \stars&amp;maybe\cr \.{unsigned}&amp;|raw_int|: \stars&amp;maybe\cr \.{using}&amp;|int_like|: \stars&amp;maybe\cr \.{va\_dcl}&amp;|decl|: \stars&amp;maybe\cr \.{va\_list}&amp;|raw_int|: \stars&amp;maybe\cr \.{virtual}&amp;|int_like|: \stars&amp;maybe\cr \.{void}&amp;|raw_int|: \stars&amp;maybe\cr \.{volatile}&amp;|const_like|: \stars&amp;maybe\cr \.{wchar\_t}&amp;|raw_int|: \stars&amp;maybe\cr \.{while}&amp;|for_like|: \stars&amp;maybe\cr \.{xor}&amp;|alfop|: \stars&amp;yes\cr \.{xor\_eq}&amp;|alfop|: \stars&amp;yes\cr \.{@@,}&amp;|insert|: \.{\\,}&amp;maybe\cr \.{@@\v}&amp;|insert|:  |opt| \.0&amp;maybe\cr \.{@@/}&amp;|insert|:  |force|&amp;no\cr \.{@@\#}&amp;|insert|:  |big_force|&amp;no\cr \.{@@+}&amp;|insert|:  |big_cancel| \.{\{\}} |break_space|   \.{\{\}} |big_cancel|&amp;no\cr \.{@@;}&amp;|semi|: &amp;maybe\cr \.{@@[@q]@&gt;}&amp;|begin_arg|: &amp;maybe\cr \.{@q[@&gt;@@]}&amp;|end_arg|: &amp;maybe\cr \.{@@\&amp;}&amp;|insert|: \.{\\J}&amp;maybe\cr \.{@@h}&amp;|insert|: |force| \.{\\ATH} |force|&amp;no\cr \.{@

@&lt;}\thinspace section name\thinspace\.{@@&gt;}&amp;|section_scrap|:
 \.{\\X}$n$\.:translated section name\.{\\X}&amp;maybe\cr
\.{@@(@q)@&gt;}\thinspace section name\thinspace\.{@@&gt;}&amp;|section_scrap|:
 \.{\\X}$n$\.{:\\.\{}section name with special characters
      quoted\.{\ \}\\X}&amp;maybe\cr
\.{/*}comment\.{*/}&amp;|insert|: |cancel|
      \.{\\C\{}translated comment\.\} |force|&amp;no\cr
\.{//}comment&amp;|insert|: |cancel|
      \.{\\SHC\{}translated comment\.\} |force|&amp;no\cr
}

\smallskip
The construction \.{@@t}\thinspace stuff\/\thinspace\.{@@&gt;} contributes
\.{\\hbox\{}\thinspace  stuff\/\thinspace\.\} to the following scrap.

@i prod.w
@* Implementing the productions. More specifically, a scrap is a structure consisting of a category |cat| and a |text_pointer| |trans|, which points to the translation in |tok_start|.  When \CEE/ text is to be processed with the grammar above, we form an array |scrap_info| containing the initial scraps. Our production rules have the nice property that the right-hand side is never longer than the left-hand side. Therefore it is convenient to use sequential allocation for the current sequence of scraps. Five pointers are used to manage the parsing:  \yskip\hang |pp| is a pointer into |scrap_info|.  We will try to match the category codes |pp-&gt;cat,@,@,(pp+1)-&gt;cat|$,\,\,\ldots\,$ to the left-hand sides of productions.  \yskip\hang |scrap_base|, |lo_ptr|, |hi_ptr|, and |scrap_ptr| are such that the current sequence of scraps appears in positions |scrap_base| through |lo_ptr| and |hi_ptr| through |scrap_ptr|, inclusive, in the |cat| and |trans| arrays. Scraps located between |scrap_base| and |lo_ptr| have been examined, while those in positions |&gt;=hi_ptr| have not yet been looked at by the parsing process.  \yskip\noindent Initially |scrap_ptr| is set to the position of the final scrap to be parsed, and it doesn't change its value. The parsing process makes sure that |lo_ptr&gt;=pp+3|, since productions have as many as four terms, by moving scraps from |hi_ptr| to |lo_ptr|. If there are fewer than |pp+3| scraps left, the positions up to |pp+3| are filled with blanks that will not match in any productions. Parsing stops when |pp==lo_ptr+1| and |hi_ptr==scrap_ptr+1|.  Since the |scrap| structure will later be used for other purposes, we declare its second element as a union.

@&lt;Typedef declarations@&gt;=
typedef struct {
  eight_bits cat;
  eight_bits mathness;
  union {
    text_pointer Trans;
    @&lt;Rest of |trans_plus| union@&gt;@;
  } trans_plus;
} scrap;
typedef scrap *scrap_pointer;@ @d trans trans_plus.Trans /* translation texts of scraps */

@&lt;Global variables@&gt;=
scrap scrap_info[max_scraps]; /* memory array for scraps */
scrap_pointer scrap_info_end=scrap_info+max_scraps -1; /* end of |scrap_info| */
scrap_pointer pp; /* current position for reducing productions */
scrap_pointer scrap_base; /* beginning of the current scrap sequence */
scrap_pointer scrap_ptr; /* ending of the current scrap sequence */
scrap_pointer lo_ptr; /* last scrap that has been examined */
scrap_pointer hi_ptr; /* first scrap that has not been examined */
scrap_pointer max_scr_ptr; /* largest value assumed by |scrap_ptr| */

@ @&lt;Set init...@&gt;=
scrap_base=scrap_info+1;
max_scr_ptr=scrap_ptr=scrap_info;
@ Token lists in |@!tok_mem| are composed of the following kinds of items for \TEX/ output.  \yskip\item{$\bullet$}Character codes and special codes like |force| and |math_rel| represent themselves;  \item{$\bullet$}|id_flag+p| represents \.{\\\\\{{\rm identifier $p$}\}};  \item{$\bullet$}|res_flag+p| represents \.{\\\&amp;\{{\rm identifier $p$}\}};  \item{$\bullet$}|section_flag+p| represents section name |p|;  \item{$\bullet$}|tok_flag+p| represents token list number |p|;  \item{$\bullet$}|inner_tok_flag+p| represents token list number |p|, to be translated without line-break controls.

@d id_flag 10240 /* signifies an identifier */
@d res_flag 2*id_flag /* signifies a reserved word */
@d section_flag 3*id_flag /* signifies a section name */
@d tok_flag 4*id_flag /* signifies a token list */
@d inner_tok_flag 5*id_flag /* signifies a token list in `\pb' */

@c
void
print_text(p) /* prints a token list for debugging; not used in |main| */
text_pointer p;
{
  token_pointer j; /* index into |tok_mem| */
  sixteen_bits r; /* remainder of token after the flag has been stripped off */
  if (p&gt;=text_ptr) printf("BAD");
  else for (j=*p; j&lt;*(p+1); j++) {
    r=*j%id_flag;
    switch (*j/id_flag) {
      case 1: printf("\\\\{"@q}@&gt;); print_id((name_dir+r)); printf(@q{@&gt;"}");
        break; /* |id_flag| */
      case 2: printf("\\&amp;{"@q}@&gt;); print_id((name_dir+r)); printf(@q{@&gt;"}");
        break; /* |res_flag| */
      case 3: printf("&lt;"); print_section_name((name_dir+r)); printf("&gt;");
        break; /* |section_flag| */
      case 4: printf("[[%d]]",r); break; /* |tok_flag| */
      case 5: printf("|[[%d]]|",r); break; /* |inner_tok_flag| */
      default: @&lt;Print token |r| in symbolic form@&gt;;
    }
  }
  fflush(stdout);
}@ @&lt;Print token |r|...@&gt;=
switch (r) {
  case math_rel: printf("\\mathrel{"@q}@&gt;); break;
  case big_cancel: printf("[ccancel]"); break;
  case cancel: printf("[cancel]"); break;
  case indent: printf("[indent]"); break;
  case outdent: printf("[outdent]"); break;
  case backup: printf("[backup]"); break;
  case opt: printf("[opt]"); break;
  case break_space: printf("[break]"); break;
  case force: printf("[force]"); break;
  case big_force: printf("[fforce]"); break;
  case preproc_line: printf("[preproc]"); break;
  case quoted_char: j++; printf("[%o]",(unsigned)*j); break;
  case end_translation: printf("[quit]"); break;
  case inserted: printf("[inserted]"); break;
  default: putxchar(r);
}
@ The production rules listed above are embedded directly into \.{CWEAVE}, since it is easier to do this than to write an interpretive system that would handle production systems in general. Several macros are defined here so that the program for each production is fairly short.  All of our productions conform to the general notion that some |k| consecutive scraps starting at some position |j| are to be replaced by a single scrap of some category |c| whose translation is composed from the translations of the disappearing scraps. After this production has been applied, the production pointer |pp| should change by an amount |d|. Such a production can be represented by the quadruple |(j,k,c,d)|. For example, the production `|exp@,comma@,exp| $\RA$ |exp|' would be represented by `|(pp,3,exp,-2)|'; in this case the pointer |pp| should decrease by 2 after the production has been applied, because some productions with |exp| in their second or third positions might now match, but no productions have |exp| in the fourth position of their left-hand sides. Note that the value of |d| is determined by the whole collection of productions, not by an individual one. The determination of |d| has been done by hand in each case, based on the full set of productions but not on the grammar of \CEE/ or on the rules for constructing the initial scraps.  We also attach a serial number to each production, so that additional information is available when debugging. For example, the program below contains the statement `|reduce(pp,3,exp,-2,4)|' when it implements the production just mentioned.  Before calling |reduce|, the program should have appended the tokens of the new translation to the |tok_mem| array. We commonly want to append copies of several existing translations, and macros are defined to simplify these common cases. For example, \\{app2}|(pp)| will append the translations of two consecutive scraps, |pp-&gt;trans| and |(pp+1)-&gt;trans|, to the current token list. If the entire new translation is formed in this way, we write `|squash(j,k,c,d,n)|' instead of `|reduce(j,k,c,d,n)|'. For example, `|squash(pp,3,exp,-2,3)|' is an abbreviation for `\\{app3}|(pp); reduce(pp,3,exp,-2,3)|'.  A couple more words of explanation: Both |big_app| and |app| append a token (while |big_app1| to |big_app4| append the specified number of scrap translations) to the current token list. The difference between |big_app| and |app| is simply that |big_app| checks whether there can be a conflict between math and non-math tokens, and intercalates a `\.{\$}' token if necessary.  When in doubt what to use, use |big_app|.  The |mathness| is an attribute of scraps that says whether they are to be printed in a math mode context or not.  It is separate from the ``part of speech'' (the |cat|) because to make each |cat| have a fixed |mathness| (as in the original \.{WEAVE}) would multiply the number of necessary production rules.  The low two bits (i.e. |mathness % 4|) control the left boundary. (We need two bits because we allow cases |yes_math|, |no_math| and |maybe_math|, which can go either way.) The next two bits (i.e. |mathness / 4|) control the right boundary. If we combine two scraps and the right boundary of the first has a different mathness from the left boundary of the second, we insert a \.{\$} in between.  Similarly, if at printing time some irreducible scrap has a |yes_math| boundary the scrap gets preceded or followed by a \.{\$}. The left boundary is |maybe_math| if and only if the right boundary is.  The code below is an exact translation of the production rules into \CEE/, using such macros, and the reader should have no difficulty understanding the format by comparing the code with the symbolic productions as they were listed earlier.

@d no_math 2 /* should be in horizontal mode */
@d yes_math 1 /* should be in math mode */
@d maybe_math 0 /* works in either horizontal or math mode */
@d big_app2(a) big_app1(a);big_app1(a+1)
@d big_app3(a) big_app2(a);big_app1(a+2)
@d big_app4(a) big_app3(a);big_app1(a+3)
@d app(a) *(tok_ptr++)=a
@d app1(a) *(tok_ptr++)=tok_flag+(int)((a)-&gt;trans-tok_start)

@&lt;Global variables@&gt;=
int cur_mathness, init_mathness;
@ @c
void
app_str(s)
char *s;
{
  while (*s) app_tok(*(s++));
}

void
big_app(a)
token a;
{
        if (a==' ' || (a&gt;=big_cancel &amp;&amp; a&lt;=big_force)) /* non-math token */ {
                if (cur_mathness==maybe_math) init_mathness=no_math;
                else if (cur_mathness==yes_math) app_str("{}$");
                cur_mathness=no_math;
        }
        else {
                if (cur_mathness==maybe_math) init_mathness=yes_math;
                else if (cur_mathness==no_math) app_str("${}");
                cur_mathness=yes_math;
        }
        app(a);
}

void
big_app1(a)
scrap_pointer a;
{
  switch (a-&gt;mathness % 4) { /* left boundary */
  case (no_math):
    if (cur_mathness==maybe_math) init_mathness=no_math;
    else if (cur_mathness==yes_math) app_str("{}$");
    cur_mathness=a-&gt;mathness / 4; /* right boundary */
    break;
  case (yes_math):
    if (cur_mathness==maybe_math) init_mathness=yes_math;
    else if (cur_mathness==no_math) app_str("${}");
    cur_mathness=a-&gt;mathness / 4; /* right boundary */
    break;
  case (maybe_math): /* no changes */ break;
  }
  app(tok_flag+(int)((a)-&gt;trans-tok_start));
}
@ In \CEE/, new specifier names can be defined via |typedef|, and we want to make the parser recognize future occurrences of the identifier thus defined as specifiers.  This is done by the procedure |make_reserved|, which changes the |ilk| of the relevant identifier.  We first need a procedure to recursively seek the first identifier in a token list, because the identifier might be enclosed in parentheses, as when one defines a function returning a pointer.  If the first identifier found is a keyword like `\&amp;{case}', we return the special value |case_found|; this prevents underlining of identifiers in case labels.  If the first identifier is the keyword `\&amp;{operator}', we give up; users who want to index definitions of overloaded \CPLUSPLUS/ operators should say, for example, `\.{@@!@@\^\\\&amp;\{operator\} \$+\{=\}\$@@&gt;}' (or, more properly alphebetized, `\.{@@!@@:operator+=\}\{\\\&amp;\{operator\} \$+\{=\}\$@@&gt;}').

@d no_ident_found (token_pointer)0 /* distinct from any identifier token */
@d case_found (token_pointer)1 /* likewise */
@d operator_found (token_pointer)2 /* likewise */

@c
token_pointer
find_first_ident(p)
text_pointer p;
{
  token_pointer q; /* token to be returned */
  token_pointer j; /* token being looked at */
  sixteen_bits r; /* remainder of token after the flag has been stripped off */
  if (p&gt;=text_ptr) confusion("find_first_ident");
  for (j=*p; j&lt;*(p+1); j++) {
    r=*j%id_flag;
    switch (*j/id_flag) {
      case 2: /* |res_flag| */
        if (name_dir[r].ilk==case_like) return case_found;
        if (name_dir[r].ilk==operator_like) return operator_found;
        if (name_dir[r].ilk!=raw_int) break;
      case 1: return j;
      case 4: case 5: /* |tok_flag| or |inner_tok_flag| */
        if ((q=find_first_ident(tok_start+r))!=no_ident_found)
          return q;
      default: ; /* char, |section_flag|, fall thru: move on to next token */
        if (*j==inserted) return no_ident_found; /* ignore inserts */
        else if (*j==qualifier) j++; /* bypass namespace qualifier */
    }
  }
  return no_ident_found;
}@ The scraps currently being parsed must be inspected for any occurrence of the identifier that we're making reserved; hence the |for| loop below.

@c
void
make_reserved(p) /* make the first identifier in |p-&gt;trans| like |int| */
scrap_pointer p;
{
  sixteen_bits tok_value; /* the name of this identifier, plus its flag*/
  token_pointer tok_loc; /* pointer to |tok_value| */
  if ((tok_loc=find_first_ident(p-&gt;trans))&lt;=operator_found)
    return; /* this should not happen */
  tok_value=*tok_loc;
  for (;p&lt;=scrap_ptr; p==lo_ptr? p=hi_ptr: p++) {
    if (p-&gt;cat==exp) {
      if (**(p-&gt;trans)==tok_value) {
        p-&gt;cat=raw_int;
        **(p-&gt;trans)=tok_value%id_flag+res_flag;
      }
    }
  }
  (name_dir+(sixteen_bits)(tok_value%id_flag))-&gt;ilk=raw_int;
  *tok_loc=tok_value%id_flag+res_flag;
}@ In the following situations we want to mark the occurrence of an identifier as a definition: when |make_reserved| is just about to be used; after a specifier, as in |char **argv|; before a colon, as in \\{found}:; and in the declaration of a function, as in \\{main}()$\{\ldots;\}$.  This is accomplished by the invocation of |make_underlined| at appropriate times.  Notice that, in the declaration of a function, we find out that the identifier is being defined only after it has been swallowed up by an |exp|.

@c
void
make_underlined(p)
/* underline the entry for the first identifier in |p-&gt;trans| */
scrap_pointer p;
{
  token_pointer tok_loc; /* where the first identifier appears */
  if ((tok_loc=find_first_ident(p-&gt;trans))&lt;=operator_found)
    return; /* this happens, for example, in |case found:| */
  xref_switch=def_flag;
  underline_xref(*tok_loc%id_flag+name_dir);
}@ We cannot use |new_xref| to underline a cross-reference at this point because this would just make a new cross-reference at the end of the list. We actually have to search through the list for the existing cross-reference.

@&lt;Predeclaration of procedures@&gt;=
void  underline_xref();

@ @c
void
underline_xref(p)
name_pointer p;
{
  xref_pointer q=(xref_pointer)p-&gt;xref; /* pointer to cross-reference being examined */
  xref_pointer r; /* temporary pointer for permuting cross-references */
  sixteen_bits m; /* cross-reference value to be installed */
  sixteen_bits n; /* cross-reference value being examined */
  if (no_xref) return;
  m=section_count+xref_switch;
  while (q != xmem) {
    n=q-&gt;num;
    if (n==m) return;
    else if (m==n+def_flag) {
        q-&gt;num=m; return;
    }
    else if (n&gt;=def_flag &amp;&amp; n&lt;m) break;
    q=q-&gt;xlink;
  }
  @&lt;Insert new cross-reference at |q|, not at beginning of list@&gt;;
}
@ We get to this section only when the identifier is one letter long, so it didn't get a non-underlined entry during phase one.  But it may have got some explicitly underlined entries in later sections, so in order to preserve the numerical order of the entries in the index, we have to insert the new cross-reference not at the beginning of the list (namely, at |p-&gt;xref|), but rather right before |q|.

@&lt;Insert new cross-reference at |q|, not at beginning of list@&gt;=
  append_xref(0); /* this number doesn't matter */
  xref_ptr-&gt;xlink=(xref_pointer)p-&gt;xref; r=xref_ptr;
  p-&gt;xref=(char*)xref_ptr;
  while (r-&gt;xlink!=q) {r-&gt;num=r-&gt;xlink-&gt;num; r=r-&gt;xlink;}
  r-&gt;num=m; /* everything from |q| on is left undisturbed */
@ Now here's the |reduce| procedure used in our code for productions.  The `|freeze_text|' macro is used to give official status to a token list. Before saying |freeze_text|, items are appended to the current token list, and we know that the eventual number of this token list will be the current value of |text_ptr|. But no list of that number really exists as yet, because no ending point for the current list has been stored in the |tok_start| array. After saying |freeze_text|, the old current token list becomes legitimate, and its number is the current value of |text_ptr-1| since |text_ptr| has been increased. The new current token list is empty and ready to be appended to. Note that |freeze_text| does not check to see that |text_ptr| hasn't gotten too large, since it is assumed that this test was done beforehand.

@d freeze_text *(++text_ptr)=tok_ptr

@c
void
reduce(j,k,c,d,n)
scrap_pointer j;
eight_bits c;
short k, d, n;
{
  scrap_pointer i, i1; /* pointers into scrap memory */
  j-&gt;cat=c; j-&gt;trans=text_ptr;
  j-&gt;mathness=4*cur_mathness+init_mathness;
  freeze_text;
  if (k&gt;1) {
    for (i=j+k, i1=j+1; i&lt;=lo_ptr; i++, i1++) {
      i1-&gt;cat=i-&gt;cat; i1-&gt;trans=i-&gt;trans;
      i1-&gt;mathness=i-&gt;mathness;
    }
    lo_ptr=lo_ptr-k+1;
  }
  pp=(pp+d&lt;scrap_base? scrap_base: pp+d);
  @&lt;Print a snapshot of the scrap list if debugging @&gt;;
  pp--; /* we next say |pp++| */
}@ Here's the |squash| procedure, which takes advantage of the simplification that occurs when |k==1|.

@c
void
squash(j,k,c,d,n)
scrap_pointer j;
eight_bits c;
short k, d, n;
{
  scrap_pointer i; /* pointers into scrap memory */
  if (k==1) {
    j-&gt;cat=c; pp=(pp+d&lt;scrap_base? scrap_base: pp+d);
    @&lt;Print a snapshot...@&gt;;
    pp--; /* we next say |pp++| */
    return;
  }
  for (i=j; i&lt;j+k; i++) big_app1(i);
  reduce(j,k,c,d,n);
}@ If \.{CWEAVE} is being run in debugging mode, the production numbers and current stack categories will be printed out when |tracing| is set to 2; a sequence of two or more irreducible scraps will be printed out when |tracing| is set to 1.

@&lt;Global variables@&gt;=
int tracing; /* can be used to show parsing details */

@ @&lt;Print a snapsh...@&gt;=
{ scrap_pointer k; /* pointer into |scrap_info| */
  if (tracing==2) {
    printf("\n%d:",n);
    for (k=scrap_base; k&lt;=lo_ptr; k++) {
      if (k==pp) putxchar('*'); else putxchar(' ');
      if (k-&gt;mathness %4 ==  yes_math) putchar('+');
      else if (k-&gt;mathness %4 ==  no_math) putchar('-');
      print_cat(k-&gt;cat);
      if (k-&gt;mathness /4 ==  yes_math) putchar('+');
      else if (k-&gt;mathness /4 ==  no_math) putchar('-');
    }
    if (hi_ptr&lt;=scrap_ptr) printf("..."); /* indicate that more is coming */
  }
}
@ The |translate| function assumes that scraps have been stored in positions |scrap_base| through |scrap_ptr| of |cat| and |trans|. It applies productions as much as possible. The result is a token list containing the translation of the given sequence of scraps.  After calling |translate|, we will have |text_ptr+3&lt;=max_texts| and |tok_ptr+6&lt;=max_toks|, so it will be possible to create up to three token lists with up to six tokens without checking for overflow. Before calling |translate|, we should have |text_ptr&lt;max_texts| and |scrap_ptr&lt;max_scraps|, since |translate| might add a new text and a new scrap before it checks for overflow.

@c
text_pointer
translate() /* converts a sequence of scraps */
{
  scrap_pointer i, /* index into |cat| */
  j; /* runs through final scraps */
  pp=scrap_base; lo_ptr=pp-1; hi_ptr=pp;
  @&lt;If tracing, print an indication of where we are@&gt;;
  @&lt;Reduce the scraps...@&gt;;
  @&lt;Combine the irreducible scraps that remain@&gt;;
}@ @&lt;If tracing,...@&gt;=
if (tracing==2) {
  printf("\nTracing after l. %d:\n",cur_line); mark_harmless;
@.Tracing after...@&gt;
  if (loc&gt;buffer+50) {
    printf("...");
    term_write(loc-51,51);
  }
  else term_write(buffer,loc-buffer);
}
@ And here now is the code that applies productions as long as possible. Before applying the production mechanism, we must make sure it has good input (at least four scraps, the length of the lhs of the longest rules), and that there is enough room in the memory arrays to hold the appended tokens and texts.  Here we use a very conservative test; it's more important to make sure the program will still work if we change the production rules (within reason) than to squeeze the last bit of space from the memory arrays.

@d safe_tok_incr 20
@d safe_text_incr 10
@d safe_scrap_incr 10

@&lt;Reduce the scraps using the productions until no more rules apply@&gt;=
while (1) {
  @&lt;Make sure the entries |pp| through |pp+3| of |cat| are defined@&gt;;
  if (tok_ptr+safe_tok_incr&gt;tok_mem_end) {
    if (tok_ptr&gt;max_tok_ptr) max_tok_ptr=tok_ptr;
    overflow("token");
  }
  if (text_ptr+safe_text_incr&gt;tok_start_end) {
    if (text_ptr&gt;max_text_ptr) max_text_ptr=text_ptr;
    overflow("text");
  }
  if (pp&gt;lo_ptr) break;
  init_mathness=cur_mathness=maybe_math;
  @&lt;Match a production...@&gt;;
}
@ If we get to the end of the scrap list, category codes equal to zero are stored, since zero does not match anything in a production.

@&lt;Make sure the entries |pp| through |pp+3| of |cat| are defined@&gt;=
if (lo_ptr&lt;pp+3) {
  while (hi_ptr&lt;=scrap_ptr &amp;&amp; lo_ptr!=pp+3) {
    (++lo_ptr)-&gt;cat=hi_ptr-&gt;cat; lo_ptr-&gt;mathness=(hi_ptr)-&gt;mathness;
    lo_ptr-&gt;trans=(hi_ptr++)-&gt;trans;
  }
  for (i=lo_ptr+1;i&lt;=pp+3;i++) i-&gt;cat=0;
}
@ Let us consider the big switch for productions now, before looking at its context. We want to design the program so that this switch works, so we might as well not keep ourselves in suspense about exactly what code needs to be provided with a proper environment.

@d cat1 (pp+1)-&gt;cat
@d cat2 (pp+2)-&gt;cat
@d cat3 (pp+3)-&gt;cat
@d lhs_not_simple (pp-&gt;cat!=public_like
        &amp;&amp; pp-&gt;cat!=semi
        &amp;&amp; pp-&gt;cat!=prelangle
        &amp;&amp; pp-&gt;cat!=prerangle
        &amp;&amp; pp-&gt;cat!=template_like
        &amp;&amp; pp-&gt;cat!=new_like
        &amp;&amp; pp-&gt;cat!=new_exp
        &amp;&amp; pp-&gt;cat!=ftemplate
        &amp;&amp; pp-&gt;cat!=raw_ubin
        &amp;&amp; pp-&gt;cat!=const_like
        &amp;&amp; pp-&gt;cat!=raw_int
        &amp;&amp; pp-&gt;cat!=operator_like)
 /* not a production with left side length 1 */

@&lt;Match a production at |pp|, or increase |pp| if there is no match@&gt;= {
  if (cat1==end_arg &amp;&amp; lhs_not_simple)
    if (pp-&gt;cat==begin_arg) squash(pp,2,exp,-2,124);
    else squash(pp,2,end_arg,-1,125);
  else if (cat1==insert) squash(pp,2,pp-&gt;cat,-2,0);
  else if (cat2==insert) squash(pp+1,2,(pp+1)-&gt;cat,-1,0);
  else if (cat3==insert) squash(pp+2,2,(pp+2)-&gt;cat,0,0);
  else
  switch (pp-&gt;cat) {
    case exp: @&lt;Cases for |exp|@&gt;; @+break;
    case lpar: @&lt;Cases for |lpar|@&gt;; @+break;
    case unop: @&lt;Cases for |unop|@&gt;; @+break;
    case ubinop: @&lt;Cases for |ubinop|@&gt;; @+break;
    case binop: @&lt;Cases for |binop|@&gt;; @+break;
    case cast: @&lt;Cases for |cast|@&gt;; @+break;
    case sizeof_like: @&lt;Cases for |sizeof_like|@&gt;; @+break;
    case int_like: @&lt;Cases for |int_like|@&gt;; @+break;
    case public_like: @&lt;Cases for |public_like|@&gt;; @+break;
    case colcol: @&lt;Cases for |colcol|@&gt;; @+break;
    case decl_head: @&lt;Cases for |decl_head|@&gt;; @+break;
    case decl: @&lt;Cases for |decl|@&gt;; @+break;
    case base: @&lt;Cases for |base|@&gt;; @+break;
    case struct_like: @&lt;Cases for |struct_like|@&gt;; @+break;
    case struct_head: @&lt;Cases for |struct_head|@&gt;; @+break;
    case fn_decl: @&lt;Cases for |fn_decl|@&gt;; @+break;
    case function: @&lt;Cases for |function|@&gt;; @+break;
    case lbrace: @&lt;Cases for |lbrace|@&gt;; @+break;
    case if_like: @&lt;Cases for |if_like|@&gt;; @+break;
    case else_like: @&lt;Cases for |else_like|@&gt;; @+break;
    case else_head: @&lt;Cases for |else_head|@&gt;; @+break;
    case if_clause: @&lt;Cases for |if_clause|@&gt;; @+break;
    case if_head: @&lt;Cases for |if_head|@&gt;; @+break;
    case do_like: @&lt;Cases for |do_like|@&gt;; @+break;
    case case_like: @&lt;Cases for |case_like|@&gt;; @+break;
    case catch_like: @&lt;Cases for |catch_like|@&gt;; @+break;
    case tag: @&lt;Cases for |tag|@&gt;; @+break;
    case stmt: @&lt;Cases for |stmt|@&gt;; @+break;
    case semi: @&lt;Cases for |semi|@&gt;; @+break;
    case lproc: @&lt;Cases for |lproc|@&gt;; @+break;
    case section_scrap: @&lt;Cases for |section_scrap|@&gt;; @+break;
    case insert: @&lt;Cases for |insert|@&gt;; @+break;
    case prelangle: @&lt;Cases for |prelangle|@&gt;; @+break;
    case prerangle: @&lt;Cases for |prerangle|@&gt;; @+break;
    case langle: @&lt;Cases for |langle|@&gt;; @+break;
    case template_like: @&lt;Cases for |template_like|@&gt;; @+break;
    case new_like: @&lt;Cases for |new_like|@&gt;; @+break;
    case new_exp: @&lt;Cases for |new_exp|@&gt;; @+break;
    case ftemplate: @&lt;Cases for |ftemplate|@&gt;; @+break;
    case for_like: @&lt;Cases for |for_like|@&gt;; @+break;
    case raw_ubin: @&lt;Cases for |raw_ubin|@&gt;; @+break;
    case const_like: @&lt;Cases for |const_like|@&gt;; @+break;
    case raw_int: @&lt;Cases for |raw_int|@&gt;; @+break;
    case operator_like: @&lt;Cases for |operator_like|@&gt;; @+break;
    case typedef_like: @&lt;Cases for |typedef_like|@&gt;; @+break;
    case delete_like: @&lt;Cases for |delete_like|@&gt;; @+break;
    case question: @&lt;Cases for |question|@&gt;; @+break;
  }
  pp++; /* if no match was found, we move to the right */
}
@ Now comes the code that tries to match each production starting with a particular type of scrap. Whenever a match is discovered, the |squash| or |reduce| macro will cause the appropriate action to be performed, followed by |goto found|.

@&lt;Cases for |exp|@&gt;=
if (cat1==lbrace || cat1==int_like || cat1==decl) {
  make_underlined(pp); big_app1(pp); big_app(indent); app(indent);
  reduce(pp,1,fn_decl,0,1);
}
else if (cat1==unop) squash(pp,2,exp,-2,2);
else if ((cat1==binop || cat1==ubinop) &amp;&amp; cat2==exp)
        squash(pp,3,exp,-2,3);
else if (cat1==comma &amp;&amp; cat2==exp) {
  big_app2(pp);
  app(opt); app('9'); big_app1(pp+2); reduce(pp,3,exp,-2,4);
}
else if (cat1==lpar &amp;&amp; cat2==rpar &amp;&amp; cat3==colon) squash(pp+3,1,base,0,5);
else if (cat1==cast &amp;&amp; cat2==colon) squash(pp+2,1,base,0,5);
else if (cat1==semi) squash(pp,2,stmt,-1,6);
else if (cat1==colon) {
  make_underlined (pp);  squash(pp,2,tag,-1,7);
}
else if (cat1==rbrace) squash(pp,1,stmt,-1,8);
else if (cat1==lpar &amp;&amp; cat2==rpar &amp;&amp; (cat3==const_like || cat3==case_like)) {
  big_app1(pp+2); big_app(' '); big_app1(pp+3); reduce(pp+2,2,rpar,0,9);
}
else if (cat1==cast &amp;&amp; (cat2==const_like || cat2==case_like)) {
  big_app1(pp+1); big_app(' '); big_app1(pp+2); reduce(pp+1,2,cast,0,9);
}
else if (cat1==exp || cat1==cast) squash(pp,2,exp,-2,10);
@ @&lt;Cases for |lpar|@&gt;=
if ((cat1==exp||cat1==ubinop) &amp;&amp; cat2==rpar) squash(pp,3,exp,-2,11);
else if (cat1==rpar) {
  big_app1(pp); app('\\'); app(','); big_app1(pp+1);
@.\\,@&gt;
  reduce(pp,2,exp,-2,12);
}
else if ((cat1==decl_head || cat1==int_like || cat1==cast) &amp;&amp; cat2==rpar)
 squash(pp,3,cast,-2,13);
else if ((cat1==decl_head || cat1==int_like || cat1==exp) &amp;&amp; cat2==comma) {
  big_app3(pp); app(opt); app('9'); reduce(pp,3,lpar,-1,14);
}
else if (cat1==stmt || cat1==decl) {
  big_app2(pp); big_app(' '); reduce(pp,2,lpar,-1,15);
}
@ @&lt;Cases for |unop|@&gt;=
if (cat1==exp || cat1==int_like) squash(pp,2,exp,-2,16);
@ @&lt;Cases for |ubinop|@&gt;=
if (cat1==cast &amp;&amp; cat2==rpar) {
  big_app('{'); big_app1(pp); big_app('}'); big_app1(pp+1);
  reduce(pp,2,cast,-2,17);
}
else if (cat1==exp || cat1==int_like) {
  big_app('{'); big_app1(pp); big_app('}'); big_app1(pp+1);
  reduce(pp,2,cat1,-2,18);
}
else if (cat1==binop) {
  big_app(math_rel); big_app1(pp); big_app('{'); big_app1(pp+1); big_app('}');
  big_app('}'); reduce(pp,2,binop,-1,19);
}
@ @&lt;Cases for |binop|@&gt;=
if (cat1==binop) {
  big_app(math_rel); big_app('{'); big_app1(pp); big_app('}');
  big_app('{'); big_app1(pp+1); big_app('}');
  big_app('}'); reduce(pp,2,binop,-1,20);
}
@ @&lt;Cases for |cast|@&gt;=
if (cat1==lpar) squash(pp,2,lpar,-1,21);
else if (cat1==exp) {
  big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,21);
}
else if (cat1==semi) squash(pp,1,exp,-2,22);
@ @&lt;Cases for |sizeof_like|@&gt;=
if (cat1==cast) squash(pp,2,exp,-2,23);
else if (cat1==exp) {
  big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,24);
}
@ @&lt;Cases for |int_like|@&gt;=
if (cat1==int_like|| cat1==struct_like) {
  big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,cat1,-2,25);
}
else if (cat1==exp &amp;&amp; (cat2==raw_int||cat2==struct_like))
  squash(pp,2,int_like,-2,26);
else if (cat1==exp || cat1==ubinop || cat1==colon) {
  big_app1(pp); big_app(' '); reduce(pp,1,decl_head,-1,27);
}
else if (cat1==semi || cat1==binop) squash(pp,1,decl_head,0,28);
@ @&lt;Cases for |public_like|@&gt;=
if (cat1==colon) squash(pp,2,tag,-1,29);
else squash(pp,1,int_like,-2,30);
@ @&lt;Cases for |colcol|@&gt;=
if (cat1==exp||cat1==int_like) {
  app(qualifier); squash(pp,2,cat1,-2,31);
}@+else if (cat1==colcol) squash(pp,2,colcol,-1,32);
@ @&lt;Cases for |decl_head|@&gt;=
if (cat1==comma) {
  big_app2(pp); big_app(' '); reduce(pp,2,decl_head,-1,33);
}
else if (cat1==ubinop) {
  big_app1(pp); big_app('{'); big_app1(pp+1); big_app('}');
  reduce(pp,2,decl_head,-1,34);
}
else if (cat1==exp &amp;&amp; cat2!=lpar &amp;&amp; cat2!=exp &amp;&amp; cat2!=cast) {
  make_underlined(pp+1); squash(pp,2,decl_head,-1,35);
}
else if ((cat1==binop||cat1==colon) &amp;&amp; cat2==exp &amp;&amp; (cat3==comma ||
    cat3==semi || cat3==rpar))
  squash(pp,3,decl_head,-1,36);
else if (cat1==cast) squash(pp,2,decl_head,-1,37);
else if (cat1==lbrace || cat1==int_like || cat1==decl) {
  big_app1(pp); big_app(indent); app(indent); reduce(pp,1,fn_decl,0,38);
}
else if (cat1==semi) squash(pp,2,decl,-1,39);
@ @&lt;Cases for |decl|@&gt;=
if (cat1==decl) {
  big_app1(pp); big_app(force); big_app1(pp+1);
  reduce(pp,2,decl,-1,40);
}
else if (cat1==stmt || cat1==function) {
  big_app1(pp); big_app(big_force);
  big_app1(pp+1); reduce(pp,2,cat1,-1,41);
}
@ @&lt;Cases for |base|@&gt;=
if (cat1==int_like || cat1==exp) {
  if (cat2==comma) {
    big_app1(pp); big_app(' '); big_app2(pp+1);
    app(opt); app('9'); reduce(pp,3,base,0,42);
  }
  else if (cat2==lbrace) {
    big_app1(pp); big_app(' '); big_app1(pp+1); big_app(' '); big_app1(pp+2);
    reduce(pp,3,lbrace,-2,43);
  }
}
@ @&lt;Cases for |struct_like|@&gt;=
if (cat1==lbrace) {
  big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,struct_head,0,44);
}
else if (cat1==exp||cat1==int_like) {
  if (cat2==lbrace || cat2==semi) {
    make_underlined(pp+1); make_reserved(pp+1);
    big_app1(pp); big_app(' '); big_app1(pp+1);
    if (cat2==semi) reduce(pp,2,decl_head,0,45);
    else {
      big_app(' '); big_app1(pp+2);reduce(pp,3,struct_head,0,46);
    }
  }
  else if (cat2==colon) squash(pp+2,1,base,2,47);
  else if (cat2!=base) {
    big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,int_like,-2,48);
  }
}
@ @&lt;Cases for |struct_head|@&gt;=
if ((cat1==decl || cat1==stmt || cat1==function) &amp;&amp; cat2==rbrace) {
  big_app1(pp); big_app(indent); big_app(force); big_app1(pp+1);
  big_app(outdent); big_app(force);  big_app1(pp+2);
  reduce(pp,3,int_like,-2,49);
}
else if (cat1==rbrace) {
  big_app1(pp); app_str("\\,"); big_app1(pp+1);
@.\\,@&gt;
  reduce(pp,2,int_like,-2,50);
}
@ @&lt;Cases for |fn_decl|@&gt;=
if (cat1==decl) {
  big_app1(pp); big_app(force); big_app1(pp+1); reduce(pp,2,fn_decl,0,51);
}
else if (cat1==stmt) {
  big_app1(pp); app(outdent); app(outdent); big_app(force);
  big_app1(pp+1); reduce(pp,2,function,-1,52);
}
@ @&lt;Cases for |function|@&gt;=
if (cat1==function || cat1==decl || cat1==stmt) {
  big_app1(pp); big_app(big_force); big_app1(pp+1); reduce(pp,2,cat1,-1,53);
}
@ @&lt;Cases for |lbrace|@&gt;=
if (cat1==rbrace) {
  big_app1(pp); app('\\'); app(','); big_app1(pp+1);
@.\\,@&gt;
  reduce(pp,2,stmt,-1,54);
}
else if ((cat1==stmt||cat1==decl||cat1==function) &amp;&amp; cat2==rbrace) {
  big_app(force); big_app1(pp);  big_app(indent); big_app(force);
  big_app1(pp+1); big_app(force); big_app(backup);  big_app1(pp+2);
  big_app(outdent); big_app(force); reduce(pp,3,stmt,-1,55);
}
else if (cat1==exp) {
  if (cat2==rbrace) squash(pp,3,exp,-2,56);
  else if (cat2==comma &amp;&amp; cat3==rbrace) squash(pp,4,exp,-2,56);
}
@ @&lt;Cases for |if_like|@&gt;=
if (cat1==exp) {
  big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,if_clause,0,57);
}
@ @&lt;Cases for |else_like|@&gt;=
if (cat1==colon) squash(pp+1,1,base,1,58);
else if (cat1==lbrace) squash(pp,1,else_head,0,59);
else if (cat1==stmt) {
  big_app(force); big_app1(pp); big_app(indent); big_app(break_space);
  big_app1(pp+1); big_app(outdent); big_app(force);
  reduce(pp,2,stmt,-1,60);
}
@ @&lt;Cases for |else_head|@&gt;=
if (cat1==stmt || cat1==exp) {
  big_app(force); big_app1(pp); big_app(break_space); app(noop);
  big_app(cancel); big_app1(pp+1); big_app(force);
  reduce(pp,2,stmt,-1,61);
}
@ @&lt;Cases for |if_clause|@&gt;=
if (cat1==lbrace) squash(pp,1,if_head,0,62);
else if (cat1==stmt) {
  if (cat2==else_like) {
    big_app(force); big_app1(pp); big_app(indent); big_app(break_space);
    big_app1(pp+1); big_app(outdent); big_app(force); big_app1(pp+2);
    if (cat3==if_like) {
      big_app(' '); big_app1(pp+3); reduce(pp,4,if_like,0,63);
    }@+else reduce(pp,3,else_like,0,64);
  }
  else squash(pp,1,else_like,0,65);
}
@ @&lt;Cases for |if_head|@&gt;=
if (cat1==stmt || cat1==exp) {
  if (cat2==else_like) {
    big_app(force); big_app1(pp); big_app(break_space); app(noop);
    big_app(cancel); big_app1(pp+1); big_app(force); big_app1(pp+2);
    if (cat3==if_like) {
      big_app(' '); big_app1(pp+3); reduce(pp,4,if_like,0,66);
    }@+else reduce(pp,3,else_like,0,67);
  }
  else squash(pp,1,else_head,0,68);
}
@ @&lt;Cases for |do_like|@&gt;=
if (cat1==stmt &amp;&amp; cat2==else_like &amp;&amp; cat3==semi) {
  big_app1(pp); big_app(break_space); app(noop); big_app(cancel);
  big_app1(pp+1); big_app(cancel); app(noop); big_app(break_space);
  big_app2(pp+2); reduce(pp,4,stmt,-1,69);
}
@ @&lt;Cases for |case_like|@&gt;=
if (cat1==semi) squash(pp,2,stmt,-1,70);
else if (cat1==colon) squash(pp,2,tag,-1,71);
else if (cat1==exp) {
  big_app1(pp); big_app(' ');  big_app1(pp+1);  reduce(pp,2,exp,-2,72);
}
@ @&lt;Cases for |catch_like|@&gt;=
if (cat1==cast || cat1==exp) {
  big_app2(pp); big_app(indent); big_app(indent); reduce(pp,2,fn_decl,0,73);
}
@ @&lt;Cases for |tag|@&gt;=
if (cat1==tag) {
  big_app1(pp); big_app(break_space); big_app1(pp+1); reduce(pp,2,tag,-1,74);
}
else if (cat1==stmt||cat1==decl||cat1==function) {
  big_app(force); big_app(backup); big_app1(pp); big_app(break_space);
  big_app1(pp+1); reduce(pp,2,cat1,-1,75);
}
@ The user can decide at run-time whether short statements should be grouped together on the same line.

@d force_lines flags['f'] /* should each statement be on its own line? */
@&lt;Cases for |stmt|@&gt;=
if (cat1==stmt||cat1==decl||cat1==function) {
  big_app1(pp);
  if (cat1==function) big_app(big_force);
  else if (cat1==decl) big_app(big_force);
  else if (force_lines) big_app(force);
  else big_app(break_space);
  big_app1(pp+1); reduce(pp,2,cat1,-1,76);
}
@ @&lt;Cases for |semi|@&gt;=
big_app(' '); big_app1(pp); reduce(pp,1,stmt,-1,77);
@ @&lt;Cases for |lproc|@&gt;=
if (cat1==define_like) make_underlined(pp+2);
if (cat1==else_like || cat1==if_like ||cat1==define_like)
  squash(pp,2,lproc,0,78);
else if (cat1==rproc) {
  app(inserted); big_app2(pp); reduce(pp,2,insert,-1,79);
} else if (cat1==exp || cat1==function) {
  if (cat2==rproc) {
    app(inserted); big_app1(pp); big_app(' '); big_app2(pp+1);
    reduce(pp,3,insert,-1,80);
  }
  else if (cat2==exp &amp;&amp; cat3==rproc &amp;&amp; cat1==exp) {
    app(inserted); big_app1(pp); big_app(' '); big_app1(pp+1); app_str(" \\5");
@.\\5@&gt;
    big_app2(pp+2); reduce(pp,4,insert,-1,80);
  }
}
@ @&lt;Cases for |section_scrap|@&gt;=
if (cat1==semi) {
  big_app2(pp); big_app(force); reduce(pp,2,stmt,-2,81);
}
else squash(pp,1,exp,-2,82);
@ @&lt;Cases for |insert|@&gt;=
if (cat1)
  squash(pp,2,cat1,0,83);
@ @&lt;Cases for |prelangle|@&gt;=
init_mathness=cur_mathness=yes_math;
app('&lt;'); reduce(pp,1,binop,-2,84);
@ @&lt;Cases for |prerangle|@&gt;=
init_mathness=cur_mathness=yes_math;
app('&gt;'); reduce(pp,1,binop,-2,85);
@ @&lt;Cases for |langle|@&gt;=
if (cat1==prerangle) {
  big_app1(pp); app('\\'); app(','); big_app1(pp+1);
@.\\,@&gt;
  reduce(pp,2,cast,-1,86);
}
else if (cat1==decl_head || cat1==int_like || cat1==exp) {
  if (cat2==prerangle) squash(pp,3,cast,-1,87);
  else if (cat2==comma) {
    big_app3(pp); app(opt); app('9'); reduce(pp,3,langle,0,88);
  }
}
@ @&lt;Cases for |template_like|@&gt;=
if (cat1==exp &amp;&amp; cat2==prelangle) squash(pp+2,1,langle,2,89);
else if (cat1==exp || cat1==raw_int) {
  big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,cat1,-2,90);
}@+ else squash(pp,1,raw_int,0,91);
@ @&lt;Cases for |new_like|@&gt;=
if (cat1==lpar &amp;&amp; cat2==exp &amp;&amp; cat3==rpar) squash(pp,4,new_like,0,92);
else if (cat1==cast) {
  big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,93);
}
else if (cat1!=lpar) squash(pp,1,new_exp,0,94);
@ @&lt;Cases for |new_exp|@&gt;=
if (cat1==int_like || cat1==const_like) {
  big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,new_exp,0,95);
}
else if (cat1==struct_like &amp;&amp; (cat2==exp || cat2==int_like)) {
  big_app1(pp); big_app(' '); big_app1(pp+1); big_app(' ');
  big_app1(pp+2); reduce(pp,3,new_exp,0,96);
}
else if (cat1==raw_ubin) {
  big_app1(pp); big_app('{'); big_app1(pp+1); big_app('}');
  reduce(pp,2,new_exp,0,97);
}
else if (cat1==lpar) squash(pp,1,exp,-2,98);
else if (cat1==exp) {
  big_app1(pp); big_app(' '); reduce(pp,1,exp,-2,98);
}
else if (cat1!=raw_int &amp;&amp; cat1!=struct_like &amp;&amp; cat1!=colcol)
  squash(pp,1,exp,-2,99);
@ @&lt;Cases for |ftemplate|@&gt;=
if (cat1==prelangle) squash(pp+1,1,langle,1,100);
else squash(pp,1,exp,-2,101);
@ @&lt;Cases for |for_like|@&gt;=
if (cat1==exp) {
  big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,else_like,-2,102);
}
@ @&lt;Cases for |raw_ubin|@&gt;=
if (cat1==const_like) {
  big_app2(pp); app_str("\\ "); reduce(pp,2,raw_ubin,0,103);
@.\\\ @&gt;
} else squash(pp,1,ubinop,-2,104);
@ @&lt;Cases for |const_like|@&gt;=
squash(pp,1,int_like,-2,105);
@ @&lt;Cases for |raw_int|@&gt;=
if (cat1==prelangle) squash(pp+1,1,langle,1,106);
else if (cat1==colcol) squash(pp,2,colcol,-1,107);
else if (cat1==cast) squash(pp,2,raw_int,0,108);
else if (cat1==lpar) squash(pp,1,exp,-2,109);
else if (cat1!=langle) squash(pp,1,int_like,-3,110);
@ @&lt;Cases for |operator_like|@&gt;=
if (cat1==binop || cat1==unop || cat1==ubinop) {
  if (cat2==binop) break;
  big_app1(pp); big_app('{'); big_app1(pp+1); big_app('}');
  reduce(pp,2,exp,-2,111);
}
else if (cat1==new_like || cat1==delete_like) {
  big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,112);
}
else if (cat1==comma) squash(pp,2,exp,-2,113);
else if (cat1!=raw_ubin) squash(pp,1,new_exp,0,114);
@ @&lt;Cases for |typedef_like|@&gt;=
if ((cat1==int_like || cat1==cast) &amp;&amp; (cat2==comma || cat2==semi))
  squash(pp+1,1,exp,-1,115);
else if (cat1==int_like) {
  big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,typedef_like,0,116);
}
else if (cat1==exp &amp;&amp; cat2!=lpar &amp;&amp; cat2!=exp &amp;&amp; cat2!=cast) {
  make_underlined(pp+1); make_reserved(pp+1);
  big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,typedef_like,0,117);
}
else if (cat1==comma) {
  big_app2(pp); big_app(' '); reduce(pp,2,typedef_like,0,118);
}
else if (cat1==semi) squash(pp,2,decl,-1,119);
else if (cat1==ubinop &amp;&amp; (cat2==ubinop || cat2==cast)) {
  big_app('{'); big_app1(pp+1); big_app('}'); big_app1(pp+2);
  reduce(pp+1,2,cat2,0,120);
}
@ @&lt;Cases for |delete_like|@&gt;=
if (cat1==lpar &amp;&amp; cat2==rpar) {
  big_app2(pp); app('\\'); app(','); big_app1(pp+2);
@.\\,@&gt;
  reduce(pp,3,delete_like,0,121);
}
else if (cat1==exp) {
  big_app1(pp); big_app(' '); big_app1(pp+1); reduce(pp,2,exp,-2,122);
}
@ @&lt;Cases for |question|@&gt;=
if (cat1==exp &amp;&amp; (cat2==colon || cat2==base)) {
  (pp+2)-&gt;mathness=5*yes_math; /* this colon should be in math mode */
  squash(pp,3,binop,-2,123);
}
@ If the initial sequence of scraps does not reduce to a single scrap, we concatenate the translations of all remaining scraps, separated by blank spaces, with dollar signs surrounding the translations of scraps where appropriate.

@&lt;Combine the irreducible scraps that remain@&gt;= {
  @&lt;If semi-tracing, show the irreducible scraps@&gt;;
  for (j=scrap_base; j&lt;=lo_ptr; j++) {
    if (j!=scrap_base) app(' ');
    if (j-&gt;mathness % 4 == yes_math) app('$');
    app1(j);
    if (j-&gt;mathness / 4 == yes_math) app('$');
    if (tok_ptr+6&gt;tok_mem_end) overflow("token");
  }
  freeze_text; return(text_ptr-1);
}
@ @&lt;If semi-tracing, show the irreducible scraps@&gt;=
if (lo_ptr&gt;scrap_base &amp;&amp; tracing==1) {
  printf("\nIrreducible scrap sequence in section %d:",section_count);
@.Irreducible scrap sequence...@&gt;
  mark_harmless;
  for (j=scrap_base; j&lt;=lo_ptr; j++) {
    printf(" "); print_cat(j-&gt;cat);
  }
}
@* Initializing the scraps. If we are going to use the powerful production mechanism just developed, we must get the scraps set up in the first place, given a \CEE/ text. A table of the initial scraps corresponding to \CEE/ tokens appeared above in the section on parsing; our goal now is to implement that table. We shall do this by implementing a subroutine called |C_parse| that is analogous to the |C_xref| routine used during phase one.  Like |C_xref|, the |C_parse| procedure starts with the current value of |next_control| and it uses the operation |next_control=get_next()| repeatedly to read \CEE/ text until encountering the next `\.{\v}' or `\.{/*}', or until |next_control&gt;=format_code|. The scraps corresponding to what it reads are appended into the |cat| and |trans| arrays, and |scrap_ptr| is advanced.

@c
void
C_parse(spec_ctrl) /* creates scraps from \CEE/ tokens */
  eight_bits spec_ctrl;
{
  int count; /* characters remaining before string break */
  while (next_control&lt;format_code || next_control==spec_ctrl) {
    @&lt;Append the scrap appropriate to |next_control|@&gt;;
    next_control=get_next();
    if (next_control=='|' || next_control==begin_comment ||
        next_control==begin_short_comment) return;
  }
}@ The following macro is used to append a scrap whose tokens have just been appended:

@d app_scrap(c,b) {
  (++scrap_ptr)-&gt;cat=(c); scrap_ptr-&gt;trans=text_ptr;
  scrap_ptr-&gt;mathness=5*(b); /* no no, yes yes, or maybe maybe */
  freeze_text;
}
@ @&lt;Append the scr...@&gt;=
@&lt;Make sure that there is room for the new scraps, tokens, and texts@&gt;;
switch (next_control) {
  case section_name:
    app(section_flag+(int)(cur_section-name_dir));
    app_scrap(section_scrap,maybe_math);
    app_scrap(exp,yes_math);@+break;
  case string: case constant: case verbatim: @&lt;Append a string or constant@&gt;;
   @+break;
  case identifier: app_cur_id(1);@+break;
  case TeX_string: @&lt;Append a \TEX/ string, without forming a scrap@&gt;;@+break;
  case '/': case '.':
    app(next_control); app_scrap(binop,yes_math);@+break;
  case '&lt;': app_str("\\langle");@+app_scrap(prelangle,yes_math);@+break;
@.\\langle@&gt;
  case '&gt;': app_str("\\rangle");@+app_scrap(prerangle,yes_math);@+break;
@.\\rangle@&gt;
  case '=': app_str("\\K"); app_scrap(binop,yes_math);@+break;
@.\\K@&gt;
  case '|': app_str("\\OR"); app_scrap(binop,yes_math);@+break;
@.\\OR@&gt;
  case '^': app_str("\\XOR"); app_scrap(binop,yes_math);@+break;
@.\\XOR@&gt;
  case '%': app_str("\\MOD"); app_scrap(binop,yes_math);@+break;
@.\\MOD@&gt;
  case '!': app_str("\\R"); app_scrap(unop,yes_math);@+break;
@.\\R@&gt;
  case '~': app_str("\\CM"); app_scrap(unop,yes_math);@+break;
@.\\CM@&gt;
  case '+': case '-': app(next_control); app_scrap(ubinop,yes_math);@+break;
  case '*': app(next_control); app_scrap(raw_ubin,yes_math);@+break;
  case '&amp;': app_str("\\AND"); app_scrap(raw_ubin,yes_math);@+break;
@.\\AND@&gt;
  case '?': app_str("\\?"); app_scrap(question,yes_math);@+break;
@.\\?@&gt;
  case '#': app_str("\\#"); app_scrap(ubinop,yes_math);@+break;
@.\\\#@&gt;
  case ignore: case xref_roman: case xref_wildcard:
  case xref_typewriter: case noop:@+break;
  case '(': case '[': app(next_control); app_scrap(lpar,maybe_math);@+break;
  case ')': case ']': app(next_control); app_scrap(rpar,maybe_math);@+break;
  case '{': app_str("\\{"@q}@&gt;); app_scrap(lbrace,yes_math);@+break;
@.\\\{@&gt;@q}@&gt;
  case '}': app_str(@q{@&gt;"\\}"); app_scrap(rbrace,yes_math);@+break;
@q{@&gt;@.\\\}@&gt;
  case ',': app(','); app_scrap(comma,yes_math);@+break;
  case ';': app(';'); app_scrap(semi,maybe_math);@+break;
  case ':': app(':'); app_scrap(colon,no_math);@+break;@/
  @t\4@&gt;  @&lt;Cases involving nonstandard characters@&gt;@;
  case thin_space: app_str("\\,"); app_scrap(insert,maybe_math);@+break;
@.\\,@&gt;
  case math_break: app(opt); app_str("0");
    app_scrap(insert,maybe_math);@+break;
  case line_break: app(force); app_scrap(insert,no_math);@+break;
  case left_preproc: app(force); app(preproc_line);
    app_str("\\#"); app_scrap(lproc,no_math);@+break;
@.\\\#@&gt;
  case right_preproc: app(force); app_scrap(rproc,no_math);@+break;
  case big_line_break: app(big_force); app_scrap(insert,no_math);@+break;
  case no_line_break: app(big_cancel); app(noop); app(break_space);
    app(noop); app(big_cancel);
    app_scrap(insert,no_math);@+break;
  case pseudo_semi: app_scrap(semi,maybe_math);@+break;
  case macro_arg_open: app_scrap(begin_arg,maybe_math);@+break;
  case macro_arg_close: app_scrap(end_arg,maybe_math);@+break;
  case join: app_str("\\J"); app_scrap(insert,no_math);@+break;
@.\\J@&gt;
  case output_defs_code: app(force); app_str("\\ATH"); app(force);
    app_scrap(insert,no_math);@+break;
@.\\ATH@&gt;
  default: app(inserted); app(next_control);
    app_scrap(insert,maybe_math);@+break;
}
@ @&lt;Make sure that there is room for the new...@&gt;=
if (scrap_ptr+safe_scrap_incr&gt;scrap_info_end ||
  tok_ptr+safe_tok_incr&gt;tok_mem_end @| ||
  text_ptr+safe_text_incr&gt;tok_start_end) {
  if (scrap_ptr&gt;max_scr_ptr) max_scr_ptr=scrap_ptr;
  if (tok_ptr&gt;max_tok_ptr) max_tok_ptr=tok_ptr;
  if (text_ptr&gt;max_text_ptr) max_text_ptr=text_ptr;
  overflow("scrap/token/text");
}
@ Some nonstandard characters may have entered \.{CWEAVE} by means of standard ones. They are converted to \TEX/ control sequences so that it is possible to keep \.{CWEAVE} from outputting unusual |char| codes.

@&lt;Cases involving nonstandard characters@&gt;=
case not_eq: app_str("\\I");@+app_scrap(binop,yes_math);@+break;
@.\\I@&gt;
case lt_eq: app_str("\\Z");@+app_scrap(binop,yes_math);@+break;
@.\\Z@&gt;
case gt_eq: app_str("\\G");@+app_scrap(binop,yes_math);@+break;
@.\\G@&gt;
case eq_eq: app_str("\\E");@+app_scrap(binop,yes_math);@+break;
@.\\E@&gt;
case and_and: app_str("\\W");@+app_scrap(binop,yes_math);@+break;
@.\\W@&gt;
case or_or: app_str("\\V");@+app_scrap(binop,yes_math);@+break;
@.\\V@&gt;
case plus_plus: app_str("\\PP");@+app_scrap(unop,yes_math);@+break;
@.\\PP@&gt;
case minus_minus: app_str("\\MM");@+app_scrap(unop,yes_math);@+break;
@.\\MM@&gt;
case minus_gt: app_str("\\MG");@+app_scrap(binop,yes_math);@+break;
@.\\MG@&gt;
case gt_gt: app_str("\\GG");@+app_scrap(binop,yes_math);@+break;
@.\\GG@&gt;
case lt_lt: app_str("\\LL");@+app_scrap(binop,yes_math);@+break;
@.\\LL@&gt;
case dot_dot_dot: app_str("\\,\\ldots\\,");@+app_scrap(raw_int,yes_math);
  @+break;
@.\\,@&gt;
@.\\ldots@&gt;
case colon_colon: app_str("\\DC");@+app_scrap(colcol,maybe_math);@+break;
@.\\DC@&gt;
case period_ast: app_str("\\PA");@+app_scrap(binop,yes_math);@+break;
@.\\PA@&gt;
case minus_gt_ast: app_str("\\MGA");@+app_scrap(binop,yes_math);@+break;
@.\\MGA@&gt;
@ The following code must use |app_tok| instead of |app| in order to protect against overflow. Note that |tok_ptr+1&lt;=max_toks| after |app_tok| has been used, so another |app| is legitimate before testing again.  Many of the special characters in a string must be prefixed by `\.\\' so that \TEX/ will print them properly. @^special string characters@&gt;

@&lt;Append a string or constant@&gt;=
count= -1;
if (next_control==constant) app_str("\\T{"@q}@&gt;);
@.\\T@&gt;
else if (next_control==string) {
  count=20; app_str("\\.{"@q}@&gt;);
}
@.\\.@&gt;
else app_str("\\vb{"@q}@&gt;);
@.\\vb@&gt;
while (id_first&lt;id_loc) {
  if (count==0) { /* insert a discretionary break in a long string */
     app_str(@q(@&gt;@q{@&gt;"}\\)\\.{"@q}@&gt;); count=20;
@q(@&gt;@.\\)@&gt;
  }
@^high-bit character handling@&gt;
  if((eight_bits)(*id_first)&gt;0177) {
    app_tok(quoted_char);
    app_tok((eight_bits)(*id_first++));
  }
  else {
    switch (*id_first) {
      case  ' ':case '\\':case '#':case '%':case '$':case '^':
      case '{': case '}': case '~': case '&amp;': case '_': app('\\'); break;
@.\\\ @&gt;
@.\\\\@&gt;
@.\\\#@&gt;
@.\\\%@&gt;
@.\\\$@&gt;
@.\\\^@&gt;
@.\\\{@&gt;@q}@&gt;
@q{@&gt;@.\\\}@&gt;
@.\\\~@&gt;
@.\\\&amp;@&gt;
@.\\\_@&gt;
      case '@@': if (*(id_first+1)=='@@') id_first++;
        else err_print("! Double @@ should be used in strings");
@.Double @@ should be used...@&gt;
    }
    app_tok(*id_first++);
  }
  count--;
}
app(@q{@&gt;'}');
app_scrap(exp,maybe_math);
@ We do not make the \TEX/ string into a scrap, because there is no telling what the user will be putting into it; instead we leave it open, to be picked up by the next scrap. If it comes at the end of a section, it will be made into a scrap when |finish_C| is called.  There's a known bug here, in cases where an adjacent scrap is |prelangle| or |prerangle|. Then the \TEX/ string can disappear when the \.{\\langle} or \.{\\rangle} becomes \.{&lt;} or \.{&gt;}. For example, if the user writes \.{\v x&lt;@@ty@@&gt;\v}, the \TEX/ string \.{\\hbox\{y\}} eventually becomes part of an |insert| scrap, which is combined with a |prelangle| scrap and eventually lost. The best way to work around this bug is probably to enclose the \.{@@t...@@&gt;} in \.{@@[...@@]} so that the \TEX/ string is treated as an expression. @^bug, known@&gt;

@&lt;Append a \TEX/ string, without forming a scrap@&gt;=
app_str("\\hbox{"@q}@&gt;);
@^high-bit character handling@&gt;
while (id_first&lt;id_loc)
  if((eight_bits)(*id_first)&gt;0177) {
    app_tok(quoted_char);
    app_tok((eight_bits)(*id_first++));
  }
  else {
    if (*id_first=='@@') id_first++;
    app_tok(*id_first++);
  }
app(@q{@&gt;'}');
@ The function |app_cur_id| appends the current identifier to the token list; it also builds a new scrap if |scrapping==1|.

@&lt;Predeclaration of procedures@&gt;=
void app_cur_id();

@ @c
void
app_cur_id(scrapping)
boolean scrapping; /* are we making this into a scrap? */
{
  name_pointer p=id_lookup(id_first,id_loc,normal);
  if (p-&gt;ilk&lt;=custom) { /* not a reserved word */
    app(id_flag+(int)(p-name_dir));
    if (scrapping) app_scrap(p-&gt;ilk==func_template? ftemplate: exp,
                             p-&gt;ilk==custom? yes_math: maybe_math);
@.\\NULL@&gt;
  } else {
    app(res_flag+(int)(p-name_dir));
    if (scrapping) {
      if (p-&gt;ilk==alfop) app_scrap(ubinop,yes_math)@;
      else app_scrap(p-&gt;ilk,maybe_math);
    }
  }
}
@ When the `\.{\v}' that introduces \CEE/ text is sensed, a call on |C_translate| will return a pointer to the \TEX/ translation of that text. If scraps exist in |scrap_info|, they are unaffected by this translation process.

@c
text_pointer
C_translate()
{
  text_pointer p; /* points to the translation */
  scrap_pointer save_base; /* holds original value of |scrap_base| */
  save_base=scrap_base; scrap_base=scrap_ptr+1;
  C_parse(section_name); /* get the scraps together */
  if (next_control!='|') err_print("! Missing '|' after C text");
@.Missing '|'...@&gt;
  app_tok(cancel); app_scrap(insert,maybe_math);
        /* place a |cancel| token as a final ``comment'' */
  p=translate(); /* make the translation */
  if (scrap_ptr&gt;max_scr_ptr) max_scr_ptr=scrap_ptr;
  scrap_ptr=scrap_base-1; scrap_base=save_base; /* scrap the scraps */
  return(p);
}@ The |outer_parse| routine is to |C_parse| as |outer_xref| is to |C_xref|: It constructs a sequence of scraps for \CEE/ text until |next_control&gt;=format_code|. Thus, it takes care of embedded comments.  The token list created from within `\pb' brackets is output as an argument to \.{\\PB}, if the user has invoked \.{CWEAVE} with the \.{+e} flag. Although \.{cwebmac} ignores \.{\\PB}, other macro packages might use it to localize the special meaning of the macros that mark up program text.

@d make_pb flags['e']

@c
void
outer_parse() /* makes scraps from \CEE/ tokens and comments */
{
  int bal; /* brace level in comment */
  text_pointer p, q; /* partial comments */
  while (next_control&lt;format_code)
    if (next_control!=begin_comment &amp;&amp; next_control!=begin_short_comment)
      C_parse(ignore);
    else {
      boolean is_long_comment=(next_control==begin_comment);
      @&lt;Make sure that there is room for the new...@&gt;;
      app(cancel); app(inserted);
      if (is_long_comment) app_str("\\C{"@q}@&gt;);
@.\\C@&gt;
      else app_str("\\SHC{"@q}@&gt;);
@.\\SHC@&gt;
      bal=copy_comment(is_long_comment,1); next_control=ignore;
      while (bal&gt;0) {
        p=text_ptr; freeze_text; q=C_translate();
         /* at this point we have |tok_ptr+6&lt;=max_toks| */
        app(tok_flag+(int)(p-tok_start));
        if (make_pb) app_str("\\PB{");
@.\\PB@&gt;
        app(inner_tok_flag+(int)(q-tok_start));
        if (make_pb)  app_tok('}');
        if (next_control=='|') {
          bal=copy_comment(is_long_comment,bal);
          next_control=ignore;
        }
        else bal=0; /* an error has been reported */
      }
      app(force); app_scrap(insert,no_math);
        /* the full comment becomes a scrap */
    }
}@* Output of tokens.
So far our programs have only built up multi-layered token lists in
\.{CWEAVE}'s internal memory; we have to figure out how to get them into
the desired final form. The job of converting token lists to characters in
the \TEX/ output file is not difficult, although it is an implicitly
recursive process. Four main considerations had to be kept in mind when
this part of \.{CWEAVE} was designed.  (a) There are two modes of output:
|outer| mode, which translates tokens like |force| into line-breaking
control sequences, and |inner| mode, which ignores them except that blank
spaces take the place of line breaks. (b) The |cancel| instruction applies
to adjacent token or tokens that are output, and this cuts across levels
of recursion since `|cancel|' occurs at the beginning or end of a token
list on one level. (c) The \TEX/ output file will be semi-readable if line
breaks are inserted after the result of tokens like |break_space| and
|force|.  (d) The final line break should be suppressed, and there should
be no |force| token output immediately after `\.{\\Y\\B}'.@ The output process uses a stack to keep track of what is going on at different ``levels'' as the token lists are being written out. Entries on this stack have three parts:  \yskip\hang |end_field| is the |tok_mem| location where the token list of a particular level will end;  \yskip\hang |tok_field| is the |tok_mem| location from which the next token on a particular level will be read;  \yskip\hang |mode_field| is the current mode, either |inner| or |outer|.  \yskip\noindent The current values of these quantities are referred to quite frequently, so they are stored in a separate place instead of in the |stack| array. We call the current values |cur_end|, |cur_tok|, and |cur_mode|.  The global variable |stack_ptr| tells how many levels of output are currently in progress. The end of output occurs when an |end_translation| token is found, so the stack is never empty except when we first begin the output process.

@d inner 0 /* value of |mode| for \CEE/ texts within \TEX/ texts */
@d outer 1 /* value of |mode| for \CEE/ texts in sections */

@&lt;Typedef declarations@&gt;= typedef int mode;
typedef struct {
  token_pointer end_field; /* ending location of token list */
  token_pointer tok_field; /* present location within token list */
  boolean mode_field; /* interpretation of control tokens */
} output_state;
typedef output_state *stack_pointer;

@ @d cur_end cur_state.end_field /* current ending location in |tok_mem| */
@d cur_tok cur_state.tok_field /* location of next output token in |tok_mem| */
@d cur_mode cur_state.mode_field /* current mode of interpretation */
@d init_stack stack_ptr=stack;cur_mode=outer /* initialize the stack */

@&lt;Global variables@&gt;=
output_state cur_state; /* |cur_end|, |cur_tok|, |cur_mode| */
output_state stack[stack_size]; /* info for non-current levels */
stack_pointer stack_ptr; /* first unused location in the output state stack */
stack_pointer stack_end=stack+stack_size-1; /* end of |stack| */
stack_pointer max_stack_ptr; /* largest value assumed by |stack_ptr| */

@ @&lt;Set init...@&gt;=
max_stack_ptr=stack;
@ To insert token-list |p| into the output, the |push_level| subroutine is called; it saves the old level of output and gets a new one going. The value of |cur_mode| is not changed.

@c
void
push_level(p) /* suspends the current level */
text_pointer p;
{
  if (stack_ptr==stack_end) overflow("stack");
  if (stack_ptr&gt;stack) { /* save current state */
    stack_ptr-&gt;end_field=cur_end;
    stack_ptr-&gt;tok_field=cur_tok;
    stack_ptr-&gt;mode_field=cur_mode;
  }
  stack_ptr++;
  if (stack_ptr&gt;max_stack_ptr) max_stack_ptr=stack_ptr;
  cur_tok=*p; cur_end=*(p+1);
}@ Conversely, the |pop_level| routine restores the conditions that were in force when the current level was begun. This subroutine will never be called when |stack_ptr==1|.

@c
void
pop_level()
{
  cur_end=(--stack_ptr)-&gt;end_field;
  cur_tok=stack_ptr-&gt;tok_field; cur_mode=stack_ptr-&gt;mode_field;
}@ The |get_output| function returns the next byte of output that is not a reference to a token list. It returns the values |identifier| or |res_word| or |section_code| if the next token is to be an identifier (typeset in italics), a reserved word (typeset in boldface), or a section name (typeset by a complex routine that might generate additional levels of output). In these cases |cur_name| points to the identifier or section name in question.

@&lt;Global variables@&gt;=
name_pointer cur_name;

@ @d res_word 0201 /* returned by |get_output| for reserved words */
@d section_code 0200 /* returned by |get_output| for section names */

@c
eight_bits
get_output() /* returns the next token of output */
{
  sixteen_bits a; /* current item read from |tok_mem| */
  restart: while (cur_tok==cur_end) pop_level();
  a=*(cur_tok++);
  if (a&gt;=0400) {
    cur_name=a % id_flag + name_dir;
    switch (a / id_flag) {
      case 2: return(res_word); /* |a==res_flag+cur_name| */
      case 3: return(section_code); /* |a==section_flag+cur_name| */
      case 4: push_level(a % id_flag + tok_start); goto restart;
        /* |a==tok_flag+cur_name| */
      case 5: push_level(a % id_flag + tok_start); cur_mode=inner; goto restart;
        /* |a==inner_tok_flag+cur_name| */
      default: return(identifier); /* |a==id_flag+cur_name| */
    }
  }
  return(a);
}@ The real work associated with token output is done by |make_output|. This procedure appends an |end_translation| token to the current token list, and then it repeatedly calls |get_output| and feeds characters to the output buffer until reaching the |end_translation| sentinel. It is possible for |make_output| to be called recursively, since a section name may include embedded \CEE/ text; however, the depth of recursion never exceeds one level, since section names cannot be inside of section names.  A procedure called |output_C| does the scanning, translation, and output of \CEE/ text within `\pb' brackets, and this procedure uses |make_output| to output the current token list. Thus, the recursive call of |make_output| actually occurs when |make_output| calls |output_C| while outputting the name of a section. @^recursion@&gt;

@c
void
output_C() /* outputs the current token list */
{
  token_pointer save_tok_ptr;
  text_pointer save_text_ptr;
  sixteen_bits save_next_control; /* values to be restored */
  text_pointer p; /* translation of the \CEE/ text */
  save_tok_ptr=tok_ptr; save_text_ptr=text_ptr;
  save_next_control=next_control; next_control=ignore; p=C_translate();
  app(inner_tok_flag+(int)(p-tok_start));
  if (make_pb) {
    out_str("\\PB{"); make_output(); out('}');
@.\\PB@&gt;
  }@+else make_output(); /* output the list */
  if (text_ptr&gt;max_text_ptr) max_text_ptr=text_ptr;
  if (tok_ptr&gt;max_tok_ptr) max_tok_ptr=tok_ptr;
  text_ptr=save_text_ptr; tok_ptr=save_tok_ptr; /* forget the tokens */
  next_control=save_next_control; /* restore |next_control| to original state */
}@ Here is \.{CWEAVE}'s major output handler.

@&lt;Predeclaration of procedures@&gt;=
void make_output();

@ @c
void
make_output() /* outputs the equivalents of tokens */
{
  eight_bits a, /* current output byte */
  b; /* next output byte */
  int c; /* count of |indent| and |outdent| tokens */
  char scratch[longest_name]; /* scratch area for section names */
  char *k, *k_limit; /* indices into |scratch| */
  char *j; /* index into |buffer| */
  char *p; /* index into |byte_mem| */
  char delim; /* first and last character of string being copied */
  char *save_loc, *save_limit; /* |loc| and |limit| to be restored */
  name_pointer cur_section_name; /* name of section being output */
  boolean save_mode; /* value of |cur_mode| before a sequence of breaks */
  app(end_translation); /* append a sentinel */
  freeze_text; push_level(text_ptr-1);
  while (1) {
    a=get_output();
    reswitch: switch(a) {
      case end_translation: return;
      case identifier: case res_word: @&lt;Output an identifier@&gt;; break;
      case section_code: @&lt;Output a section name@&gt;; break;
      case math_rel: out_str("\\MRL{"@q}@&gt;);
@.\\MRL@&gt;
      case noop: case inserted: break;
      case cancel: case big_cancel: c=0; b=a;
        while (1) {
          a=get_output();
          if (a==inserted) continue;
          if ((a&lt;indent &amp;&amp; !(b==big_cancel&amp;&amp;a==' ')) || a&gt;big_force) break;
          if (a==indent) c++; else if (a==outdent) c--;
          else if (a==opt) a=get_output();
        }
        @&lt;Output saved |indent| or |outdent| tokens@&gt;;
        goto reswitch;
      case indent: case outdent: case opt: case backup: case break_space:
      case force: case big_force: case preproc_line:
	  	@&lt;Output a control,look ahead in case of line breaks, possibly |goto reswitch|@&gt;; break;
      case quoted_char: out(*(cur_tok++));
      case qualifier: break;
      default: out(a); /* otherwise |a| is an ordinary character */
    }
  }
}
@ @&lt;Output saved...@&gt;=
  for (;c&gt;0;c--) out_str("\\1");
@.\\1@&gt;
  for (;c&lt;0;c++) out_str("\\2");
@.\\2@&gt;
@ The current mode does not affect the behavior of \.{CWEAVE}'s output routine except when we are outputting control tokens.

@&lt;Output a control...@&gt;=
if (a&lt;break_space || a==preproc_line) {
  if (cur_mode==outer) {
    out('\\'); out(a-cancel+'0');
@.\\1@&gt;
@.\\2@&gt;
@.\\3@&gt;
@.\\4@&gt;
@.\\8@&gt;
    if (a==opt) {
      b=get_output(); /* |opt| is followed by a digit */
      if (b!='0' || force_lines==0) out(b)@;
      else out_str("{-1}"); /* |force_lines| encourages more \.{@@\v} breaks */
    }
  } else if (a==opt) b=get_output(); /* ignore digit following |opt| */
  }
else @&lt;Look ahead for strongest line break, |goto reswitch|@&gt;
@ If several of the tokens |break_space|, |force|, |big_force| occur in a row, possibly mixed with blank spaces (which are ignored), the largest one is used. A line break also occurs in the output file, except at the very end of the translation. The very first line break is suppressed (i.e., a line break that follows `\.{\\Y\\B}').

@&lt;Look ahead for strongest line break, |goto reswitch|@&gt;= {
  b=a; save_mode=cur_mode; c=0;
  while (1) {
    a=get_output();
    if (a==inserted) continue;
    if (a==cancel || a==big_cancel) {
      @&lt;Output saved |indent| or |outdent| tokens@&gt;;
      goto reswitch; /* |cancel| overrides everything */
    }
    if ((a!=' ' &amp;&amp; a&lt;indent) || a==backup || a&gt;big_force) {
      if (save_mode==outer) {
        if (out_ptr&gt;out_buf+3 &amp;&amp; strncmp(out_ptr-3,"\\Y\\B",4)==0)
          goto reswitch;
        @&lt;Output saved |indent| or |outdent| tokens@&gt;;
        out('\\'); out(b-cancel+'0');
@.\\5@&gt;
@.\\6@&gt;
@.\\7@&gt;
        if (a!=end_translation) finish_line();
      }
      else if (a!=end_translation &amp;&amp; cur_mode==inner) out(' ');
      goto reswitch;
    }
    if (a==indent) c++;
    else if (a==outdent) c--;
    else if (a==opt) a=get_output();
    else if (a&gt;b) b=a; /* if |a==' '| we have |a&lt;b| */
  }
}
@ An identifier of length one does not have to be enclosed in braces, and it looks slightly better if set in a math-italic font instead of a (slightly narrower) text-italic font. Thus we output `\.{\\\v}\.{a}' but `\.{\\\\\{aa\}}'.

@&lt;Output an identifier@&gt;=
out('\\');
if (a==identifier) {
  if (cur_name-&gt;ilk==custom &amp;&amp; !doing_format) {
 custom_out:
    for (p=cur_name-&gt;byte_start;p&lt;(cur_name+1)-&gt;byte_start;p++)
      out(*p=='_'? 'x': *p=='$'? 'X': *p);
    break;
  } else if (is_tiny(cur_name)) out('|')@;
@.\\|@&gt;
  else { delim='.';
    for (p=cur_name-&gt;byte_start;p&lt;(cur_name+1)-&gt;byte_start;p++)
      if (xislower(*p)) { /* not entirely uppercase */
         delim='\\'; break;
      }
  out(delim);
  }
@.\\\\@&gt;
@.\\.@&gt;
}@+else if (cur_name-&gt;ilk==alfop) {
  out('X');
  goto custom_out;
}@+else out('&amp;'); /* |a==res_word| */
@.\\\&amp;@&gt;
if (is_tiny(cur_name)) {
  if (isxalpha((cur_name-&gt;byte_start)[0]))
    out('\\');
  out((cur_name-&gt;byte_start)[0]);
}
else out_name(cur_name,1);
@ The remaining part of |make_output| is somewhat more complicated. When we output a section name, we may need to enter the parsing and translation routines, since the name may contain \CEE/ code embedded in \pb\ constructions. This \CEE/ code is placed at the end of the active input buffer and the translation process uses the end of the active |tok_mem| area.

@&lt;Output a section name@&gt;= {
  out_str("\\X");
@.\\X@&gt;
  cur_xref=(xref_pointer)cur_name-&gt;xref;
  if (cur_xref-&gt;num==file_flag) {an_output=1; cur_xref=cur_xref-&gt;xlink;}
  else an_output=0;
  if (cur_xref-&gt;num&gt;=def_flag) {
    out_section(cur_xref-&gt;num-def_flag);
    if (phase==3) {
      cur_xref=cur_xref-&gt;xlink;
      while (cur_xref-&gt;num&gt;=def_flag) {
        out_str(", ");
        out_section(cur_xref-&gt;num-def_flag);
      cur_xref=cur_xref-&gt;xlink;
      }
    }
  }
  else out('0'); /* output the section number, or zero if it was undefined */
  out(':');
  if (an_output) out_str("\\.{"@q}@&gt;);
@.\\.@&gt;
  @&lt;Output the text of the section name@&gt;;
  if (an_output) out_str(@q{@&gt;" }");
  out_str("\\X");
}
@ @&lt;Output the text...@&gt;=
sprint_section_name(scratch,cur_name);
k=scratch;
k_limit=scratch+strlen(scratch);
cur_section_name=cur_name;
while (k&lt;k_limit) {
  b=*(k++);
  if (b=='@@') @&lt;Skip next character, give error if not `\.{@@}'@&gt;;
  if (an_output)
    switch (b) {
 case  ' ':case '\\':case '#':case '%':case '$':case '^':
 case '{': case '}': case '~': case '&amp;': case '_':
    out('\\'); /* falls through */
@.\\\ @&gt;
@.\\\\@&gt;
@.\\\#@&gt;
@.\\\%@&gt;
@.\\\$@&gt;
@.\\\^@&gt;
@.\\\{@&gt;@q}@&gt;
@q{@&gt;@.\\\}@&gt;
@.\\\~@&gt;
@.\\\&amp;@&gt;
@.\\\_@&gt;
 default: out(b);
    }
  else if (b!='|') out(b)
  else {
    @&lt;Copy the \CEE/ text into the |buffer| array@&gt;;
    save_loc=loc; save_limit=limit; loc=limit+2; limit=j+1;
    *limit='|'; output_C();
    loc=save_loc; limit=save_limit;
  }
}
@ @&lt;Skip next char...@&gt;=
if (*k++!='@@') {
  printf("\n! Illegal control code in section name: &lt;");
@.Illegal control code...@&gt;
  print_section_name(cur_section_name); printf("&gt; "); mark_error;
}
@ The \CEE/ text enclosed in \pb\ should not contain `\.{\v}' characters, except within strings. We put a `\.{\v}' at the front of the buffer, so that an error message that displays the whole buffer will look a little bit sensible. The variable |delim| is zero outside of strings, otherwise it equals the delimiter that began the string being copied.

@&lt;Copy the \CEE/ text into the |buffer| array@&gt;=
j=limit+1; *j='|'; delim=0;
while (1) {
  if (k&gt;=k_limit) {
    printf("\n! C text in section name didn't end: &lt;");
@.C text...didn't end@&gt;
    print_section_name(cur_section_name); printf("&gt; "); mark_error; break;
  }
  b=*(k++);
  if (b=='@@' || (b=='\\' &amp;&amp; delim!=0))
     @&lt;Copy a quoted character into the buffer@&gt;
  else {
    if (b=='\'' || b=='"')
      if (delim==0) delim=b;
      else if (delim==b) delim=0;
    if (b!='|' || delim!=0) {
      if (j&gt;buffer+long_buf_size-3) overflow("buffer");
      *(++j)=b;
    }
    else break;
  }
}
@ @&lt;Copy a quoted char...@&gt;= {
  if (j&gt;buffer+long_buf_size-4) overflow("buffer");
  *(++j)=b; *(++j)=*(k++);
}
@** Phase two processing. We have assembled enough pieces of the puzzle in order to be ready to specify the processing in \.{CWEAVE}'s main pass over the source file. Phase two is analogous to phase one, except that more work is involved because we must actually output the \TEX/ material instead of merely looking at the \.{CWEB} specifications.
@&lt;Predeclaration of procedures@&gt;=
void phase_two();

@ @c
void
phase_two() {
reset_input(); if (show_progress) printf("\nWriting the output file...");
@.Writing the output file...@&gt;
section_count=0; format_visible=1; copy_limbo();
finish_line(); flush_buffer(out_buf,0,0); /* insert a blank line, it looks nice */
while (!input_has_ended) @&lt;Translate the current section@&gt;;
}
@ The output file will contain the control sequence \.{\\Y} between non-null sections of a section, e.g., between the \TEX/ and definition parts if both are nonempty. This puts a little white space between the parts when they are printed. However, we don't want \.{\\Y} to occur between two definitions within a single section. The variables |out_line| or |out_ptr| will change if a section is non-null, so the following macros `|save_position|' and `|emit_space_if_needed|' are able to handle the situation:

@d save_position save_line=out_line; save_place=out_ptr
@d emit_space_if_needed if (save_line!=out_line || save_place!=out_ptr)
  out_str("\\Y");
  space_checked=1
@.\\Y@&gt;

@&lt;Global variables@&gt;=
int save_line; /* former value of |out_line| */
char *save_place; /* former value of |out_ptr| */
int sec_depth; /* the integer, if any, following \.{@@*} */
boolean space_checked; /* have we done |emit_space_if_needed|? */
boolean format_visible; /* should the next format declaration be output? */
boolean doing_format=0; /* are we outputting a format declaration? */
boolean group_found=0; /* has a starred section occurred? */

@ @&lt;Translate the current section@&gt;= {
  section_count++;
  @&lt;Output the code for the beginning of a new section@&gt;;
  save_position;
  @&lt;Translate the \TEX/ part of the current section@&gt;;
  @&lt;Translate the definition part of the current section@&gt;;
  @&lt;Translate the \CEE/ part of the current section@&gt;;
  @&lt;Show cross-references to this section@&gt;;
  @&lt;Output the code for the end of a section@&gt;;
}
@ Sections beginning with the \.{CWEB} control sequence `\.{@@\ }' start in the output with the \TEX/ control sequence `\.{\\M}', followed by the section number. Similarly, `\.{@

@*}' sections lead to the control sequence `\.{\\N}'. In this case there's an additional parameter, representing one plus the specified depth, immediately after the \.{\\N}. If the section has changed, we put \.{\\*} just after the section number.

@&lt;Output the code for the beginning of a new section@&gt;=
if (*(loc-1)!='*') out_str("\\M");
@.\\M@&gt;
else {
  while (*loc == ' ') loc++;
  if (*loc=='*') { /* ``top'' level */
    sec_depth = -1;
    loc++;
  }
  else {
    for (sec_depth=0; xisdigit(*loc);loc++)
      sec_depth = sec_depth*10 + (*loc) -'0';
  }
  while (*loc == ' ') loc++; /* remove spaces before group title */
  group_found=1;
  out_str("\\N");
@.\\N@&gt;
  {@+ char s[32];@+sprintf(s,"{%d}",sec_depth+1);@+out_str(s);@+}
  if (show_progress)
  printf("*%d",section_count); update_terminal; /* print a progress report */
}
out_str("{");out_section(section_count); out_str("}");
@ In the \TEX/ part of a section, we simply copy the source text, except that index entries are not copied and \CEE/ text within \pb\ is translated.

@&lt;Translate the \TEX/ part of the current section@&gt;= do {
  next_control=copy_TeX();
  switch (next_control) {
    case '|': init_stack; output_C(); break;
    case '@@': out('@@'); break;
    case TeX_string: case noop:
    case xref_roman: case xref_wildcard: case xref_typewriter:
    case section_name: loc-=2; next_control=get_next(); /* skip to \.{@@&gt;} */
      if (next_control==TeX_string)
        err_print("! TeX string should be in C text only"); break;
@.TeX string should be...@&gt;
    case thin_space: case math_break: case ord:
    case line_break: case big_line_break: case no_line_break: case join:
    case pseudo_semi: case macro_arg_open: case macro_arg_close:
    case output_defs_code:
        err_print("! You can't do that in TeX text"); break;
@.You can't do that...@&gt;
  }
} while (next_control&lt;format_code);
@ When we get to the following code we have |next_control&gt;=format_code|, and the token memory is in its initial empty state.

@&lt;Translate the definition part of the current section@&gt;=
space_checked=0;
while (next_control&lt;=definition) { /* |format_code| or |definition| */
  init_stack;
  if (next_control==definition) @&lt;Start a macro definition@&gt;@;
  else @&lt;Start a format definition@&gt;;
  outer_parse(); finish_C(format_visible); format_visible=1;
  doing_format=0;
}
@ Keeping in line with the conventions of the \CEE/ preprocessor (and otherwise contrary to the rules of \.{CWEB}) we distinguish here between the case that `\.(' immediately follows an identifier and the case that the two are separated by a space.  In the latter case, and if the identifier is not followed by `\.(' at all, the replacement text starts immediately after the identifier.  In the former case, it starts after we scan the matching `\.)'.

@&lt;Start a macro definition@&gt;= {
  if (save_line!=out_line || save_place!=out_ptr || space_checked) app(backup);
  if(!space_checked){emit_space_if_needed;save_position;}
  app_str("\\D"); /* this will produce `\&amp;{define }' */
@.\\D@&gt;
  if ((next_control=get_next())!=identifier)
    err_print("! Improper macro definition");
@.Improper macro definition@&gt;
  else {
    app('$'); app_cur_id(0);
    if (*loc=='(')
  reswitch: switch (next_control=get_next()) {
      case '(': case ',': app(next_control); goto reswitch;
      case identifier: app_cur_id(0); goto reswitch;
      case ')': app(next_control); next_control=get_next(); break;
      default: err_print("! Improper macro definition"); break;
    }
    else next_control=get_next();
    app_str("$ "); app(break_space);
    app_scrap(dead,no_math); /* scrap won't take part in the parsing */
  }
}
@ @&lt;Start a format...@&gt;= {
  doing_format=1;
  if(*(loc-1)=='s' || *(loc-1)=='S') format_visible=0;
  if(!space_checked){emit_space_if_needed;save_position;}
  app_str("\\F"); /* this will produce `\&amp;{format }' */
@.\\F@&gt;
  next_control=get_next();
  if (next_control==identifier) {
    app(id_flag+(int)(id_lookup(id_first, id_loc,normal)-name_dir));
    app(' ');
    app(break_space); /* this is syntactically separate from what follows */
    next_control=get_next();
    if (next_control==identifier) {
      app(id_flag+(int)(id_lookup(id_first, id_loc,normal)-name_dir));
      app_scrap(exp,maybe_math); app_scrap(semi,maybe_math);
      next_control=get_next();
    }
  }
  if (scrap_ptr!=scrap_info+2) err_print("! Improper format definition");
@.Improper format definition@&gt;
}
@ Finally, when the \TEX/ and definition parts have been treated, we have |next_control&gt;=begin_C|. We will make the global variable |this_section| point to the current section name, if it has a name.

@&lt;Global variables@&gt;=
name_pointer this_section; /* the current section name, or zero */

@ @&lt;Translate the \CEE/...@&gt;=
this_section=name_dir;
if (next_control&lt;=section_name) {
  emit_space_if_needed; init_stack;
  if (next_control==begin_C) next_control=get_next();
  else {
    this_section=cur_section;
    @&lt;Check that '=' or '==' follows this section name, and emit the scraps to start the section definition@&gt;;
  }
  while  (next_control&lt;=section_name) {
    outer_parse();
    @&lt;Emit the scrap for a section name if present@&gt;;
  }
  finish_C(1);
}
@ The title of the section and an $\E$ or $\mathrel+\E$ are made into a scrap that should not take part in the parsing.

@&lt;Check that '='...@&gt;=
do next_control=get_next();
  while (next_control=='+'); /* allow optional `\.{+=}' */
if (next_control!='=' &amp;&amp; next_control!=eq_eq)
  err_print("! You need an = sign after the section name");
@.You need an = sign...@&gt;
  else next_control=get_next();
if (out_ptr&gt;out_buf+1 &amp;&amp; *out_ptr=='Y' &amp;&amp; *(out_ptr-1)=='\\') app(backup);
    /* the section name will be flush left */
@.\\Y@&gt;
app(section_flag+(int)(this_section-name_dir));
cur_xref=(xref_pointer)this_section-&gt;xref;
if(cur_xref-&gt;num==file_flag) cur_xref=cur_xref-&gt;xlink;
app_str("${}");
if (cur_xref-&gt;num!=section_count+def_flag) {
  app_str("\\mathrel+"); /*section name is multiply defined*/
  this_section=name_dir; /*so we won't give cross-reference info here*/
}
app_str("\\E"); /* output an equivalence sign */
@.\\E@&gt;
app_str("{}$");
app(force); app_scrap(dead,no_math);
        /* this forces a line break unless `\.{@@+}' follows */
@ @&lt;Emit the scrap...@&gt;=
if (next_control&lt;section_name) {
  err_print("! You can't do that in C text");
@.You can't do that...@&gt;
  next_control=get_next();
}
else if (next_control==section_name) {
  app(section_flag+(int)(cur_section-name_dir));
  app_scrap(section_scrap,maybe_math);
  next_control=get_next();
}
@ Cross references relating to a named section are given after the section ends.

@&lt;Show cross-references to this section@&gt;=
if (this_section&gt;name_dir) {
  cur_xref=(xref_pointer)this_section-&gt;xref;
  if (cur_xref-&gt;num==file_flag){an_output=1;cur_xref=cur_xref-&gt;xlink;}
  else an_output=0;
  if (cur_xref-&gt;num&gt;def_flag)
    cur_xref=cur_xref-&gt;xlink; /* bypass current section number */
  footnote(def_flag); footnote(cite_flag); footnote(0);
}
@ @&lt;Output the code for the end of a section@&gt;=
out_str("\\fi"); finish_line();
@.\\fi@&gt;
flush_buffer(out_buf,0,0); /* insert a blank line, it looks nice */
@ The |footnote| procedure gives cross-reference information about multiply defined section names (if the |flag| parameter is |def_flag|), or about references to a section name (if |flag==cite_flag|), or to its uses (if |flag==0|). It assumes that |cur_xref| points to the first cross-reference entry of interest, and it leaves |cur_xref| pointing to the first element not printed.  Typical outputs: `\.{\\A101.}'; `\.{\\Us 370\\ET1009.}'; `\.{\\As 8, 27\\*\\ETs64.}'.  Note that the output of \.{CWEAVE} is not English-specific; users may supply new definitions for the macros \.{\\A}, \.{\\As}, etc.

@&lt;Predeclaration of procedures@&gt;=
void footnote();

@ @c
void
footnote(flag) /* outputs section cross-references */
sixteen_bits flag;
{
  xref_pointer q; /* cross-reference pointer variable */
  if (cur_xref-&gt;num&lt;=flag) return;
  finish_line(); out('\\');
@.\\A@&gt;
@.\\Q@&gt;
@.\\U@&gt;
  out(flag==0? 'U': flag==cite_flag? 'Q': 'A');
  @&lt;Output all the section numbers on the reference list |cur_xref|@&gt;;
  out('.');
}
@ The following code distinguishes three cases, according as the number of cross-references is one, two, or more than two. Variable |q| points to the first cross-reference, and the last link is a zero.

@&lt;Output all the section numbers on the reference list |cur_xref|@&gt;=
q=cur_xref; if (q-&gt;xlink-&gt;num&gt;flag) out('s'); /* plural */
while (1) {
  out_section(cur_xref-&gt;num-flag);
  cur_xref=cur_xref-&gt;xlink; /* point to the next cross-reference to output */
  if (cur_xref-&gt;num&lt;=flag) break;
  if (cur_xref-&gt;xlink-&gt;num&gt;flag) out_str(", "); /* not the last */
  else {out_str("\\ET"); /* the last */
@.\\ET@&gt;
  if (cur_xref != q-&gt;xlink) out('s'); /* the last of more than two */
  }
}
@ The |finish_C| procedure outputs the translation of the current scraps, preceded by the control sequence `\.{\\B}' and followed by the control sequence `\.{\\par}'. It also restores the token and scrap memories to their initial empty state.  A |force| token is appended to the current scraps before translation takes place, so that the translation will normally end with \.{\\6} or \.{\\7} (the \TEX/ macros for |force| and |big_force|). This \.{\\6} or \.{\\7} is replaced by the concluding \.{\\par} or by \.{\\Y\\par}.

@&lt;Predeclaration of procedures@&gt;=
void finish_C();

@ @c
void
finish_C(visible) /* finishes a definition or a \CEE/ part */
  boolean visible; /* nonzero if we should produce \TEX/ output */
{
  text_pointer p; /* translation of the scraps */
  if (visible) {
    out_str("\\B"); app_tok(force); app_scrap(insert,no_math);
    p=translate();
@.\\B@&gt;
    app(tok_flag+(int)(p-tok_start)); make_output(); /* output the list */
    if (out_ptr&gt;out_buf+1)
      if (*(out_ptr-1)=='\\')
@.\\6@&gt;
@.\\7@&gt;
@.\\Y@&gt;
        if (*out_ptr=='6') out_ptr-=2;
        else if (*out_ptr=='7') *out_ptr='Y';
    out_str("\\par"); finish_line();
  }
  if (text_ptr&gt;max_text_ptr) max_text_ptr=text_ptr;
  if (tok_ptr&gt;max_tok_ptr) max_tok_ptr=tok_ptr;
  if (scrap_ptr&gt;max_scr_ptr) max_scr_ptr=scrap_ptr;
  tok_ptr=tok_mem+1; text_ptr=tok_start+1; scrap_ptr=scrap_info;
    /* forget the tokens and the scraps */
}
@** Phase three processing. We are nearly finished! \.{CWEAVE}'s only remaining task is to write out the index, after sorting the identifiers and index entries.  If the user has set the |no_xref| flag (the \.{-x} option on the command line), just finish off the page, omitting the index, section name list, and table of contents.

@&lt;Predeclaration of procedures@&gt;=
void phase_three();@ @c
void
phase_three() {
if (no_xref) {
  finish_line();
  out_str("\\end");
@.\\end@&gt;
  finish_line();
}
else {
  phase=3; if (show_progress) printf("\nWriting the index...");
@.Writing the index...@&gt;
  finish_line();
  if ((idx_file=fopen(idx_file_name,"w"))==NULL)
    fatal("! Cannot open index file ",idx_file_name);
@.Cannot open index file@&gt;
  if (change_exists) {
    @&lt;Tell about changed sections@&gt;; finish_line(); finish_line();
  }
  out_str("\\inx"); finish_line();
@.\\inx@&gt;
  active_file=idx_file; /* change active file to the index file */
  @&lt;Do the first pass of sorting@&gt;;
  @&lt;Sort and output the index@&gt;;
  finish_line(); fclose(active_file); /* finished with |idx_file| */
  active_file=tex_file; /* switch back to |tex_file| for a tic */
  out_str("\\fin"); finish_line();
@.\\fin@&gt;
  if ((scn_file=fopen(scn_file_name,"w"))==NULL)
    fatal("! Cannot open section file ",scn_file_name);
@.Cannot open section file@&gt;
  active_file=scn_file; /* change active file to section listing file */
  @&lt;Output all the section names@&gt;;
  finish_line(); fclose(active_file); /* finished with |scn_file| */
  active_file=tex_file;
  if (group_found) out_str("\\con");@+else out_str("\\end");
@.\\con@&gt;
@.\\end@&gt;
  finish_line();
  fclose(active_file);
}
if (show_happiness) printf("\nDone.");
check_complete(); /* was all of the change file used? */
}
@ Just before the index comes a list of all the changed sections, including the index section itself.

@&lt;Global variables@&gt;=
sixteen_bits k_section; /* runs through the sections */

@ @&lt;Tell about changed sections@&gt;= {
  /* remember that the index is already marked as changed */
  k_section=0;
  while (!changed_section[++k_section]);
  out_str("\\ch ");
@.\\ch@&gt;
  out_section(k_section);
  while (k_section&lt;section_count) {
    while (!changed_section[++k_section]);
    out_str(", "); out_section(k_section);
  }
  out('.');
}
@ A left-to-right radix sorting method is used, since this makes it easy to adjust the collating sequence and since the running time will be at worst proportional to the total length of all entries in the index. We put the identifiers into 102 different lists based on their first characters. (Uppercase letters are put into the same list as the corresponding lowercase letters, since we want to have `$t&lt;\\{TeX}&lt;\&amp;{to}$'.) The list for character |c| begins at location |bucket[c]| and continues through the |blink| array.

@&lt;Global variables@&gt;=
name_pointer bucket[256];
name_pointer next_name; /* successor of |cur_name| when sorting */
name_pointer blink[max_names]; /* links in the buckets */

@ To begin the sorting, we go through all the hash lists and put each entry having a nonempty cross-reference list into the proper bucket.

@&lt;Do the first pass of sorting@&gt;= {
int c;
for (c=0; c&lt;=255; c++) bucket[c]=NULL;
for (h=hash; h&lt;=hash_end; h++) {
  next_name=*h;
  while (next_name) {
    cur_name=next_name; next_name=cur_name-&gt;link;
    if (cur_name-&gt;xref!=(char*)xmem) {
      c=(eight_bits)((cur_name-&gt;byte_start)[0]);
      if (xisupper(c)) c=tolower(c);
      blink[cur_name-name_dir]=bucket[c]; bucket[c]=cur_name;
    }
  }
}
}
@ During the sorting phase we shall use the |cat| and |trans| arrays from
\.{CWEAVE}'s parsing algorithm and rename them |depth| and |head|. They now
represent a stack of identifier lists for all the index entries that have
not yet been output. The variable |sort_ptr| tells how many such lists are
present; the lists are output in reverse order (first |sort_ptr|, then
|sort_ptr-1|, etc.). The |j|th list starts at |head[j]|, and if the first
|k| characters of all entries on this list are known to be equal we have
|depth[j]==k|.

@ @&lt;Rest of |trans_plus| union@&gt;=
name_pointer Head;

@ @d depth cat /* reclaims memory that is no longer needed for parsing */
@d head trans_plus.Head /* ditto */
@f sort_pointer int
@d sort_pointer scrap_pointer /* ditto */
@d sort_ptr scrap_ptr /* ditto */
@d max_sorts max_scraps /* ditto */

@&lt;Global variables@&gt;=
eight_bits cur_depth; /* depth of current buckets */
char *cur_byte; /* index into |byte_mem| */
sixteen_bits cur_val; /* current cross-reference number */
sort_pointer max_sort_ptr; /* largest value of |sort_ptr| */

@ @&lt;Set init...@&gt;=
max_sort_ptr=scrap_info;

@ The desired alphabetic order is specified by the |collate| array; namely, $|collate|[0]&lt;|collate|[1]&lt;\cdots&lt;|collate|[100]$.

@&lt;Global variables@&gt;=
eight_bits collate[102+128]; /* collation order */
@^high-bit character handling@&gt;

@ We use the order $\hbox{null}&lt;\.\ &lt;\hbox{other characters}&lt;{}$\.\_${}&lt; \.A=\.a&lt;\cdots&lt;\.Z=\.z&lt;\.0&lt;\cdots&lt;\.9.$ Warning: The collation mapping needs to be changed if ASCII code is not being used. @^ASCII code dependencies@&gt; @^high-bit character handling@&gt;  We initialize |collate| by copying a few characters at a time, because some \CEE/ compilers choke on long strings.

@&lt;Set initial values@&gt;=
collate[0]=0;
strcpy(collate+1," \1\2\3\4\5\6\7\10\11\12\13\14\15\16\17");
/* 16 characters + 1 = 17 */
strcpy(collate+17,"\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37");
/* 16 characters + 17 = 33 */
strcpy(collate+33,"!\42#$%&amp;'()*+,-./:;&lt;=&gt;?@@[\\]^`{|}~_");
/* 32 characters + 33 = 65 */
strcpy(collate+65,"abcdefghijklmnopqrstuvwxyz0123456789");
/* (26 + 10) characters + 65 = 101 */
strcpy(collate+101,"\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217");
/* 16 characters + 101 = 117 */
strcpy(collate+117,"\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237");
/* 16 characters + 117 = 133 */
strcpy(collate+133,"\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257");
/* 16 characters + 133 = 149 */
strcpy(collate+149,"\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277");
/* 16 characters + 149 = 165 */
strcpy(collate+165,"\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317");
/* 16 characters + 165 = 181 */
strcpy(collate+181,"\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337");
/* 16 characters + 181 = 197 */
strcpy(collate+197,"\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357");
/* 16 characters + 197 = 213 */
strcpy(collate+213,"\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377");
/* 16 characters + 213 = 229 */

@ @&lt;Sort and output...@&gt;=
sort_ptr=scrap_info; unbucket(1);
while (sort_ptr&gt;scrap_info) {
  cur_depth=sort_ptr-&gt;depth;
  if (blink[sort_ptr-&gt;head-name_dir]==0 || cur_depth==infinity)
    @&lt;Output index entries for the list at |sort_ptr|@&gt;@;
  else @&lt;Split the list at |sort_ptr| into further lists@&gt;;
}
@ @&lt;Split the list...@&gt;= {
  eight_bits c;
  next_name=sort_ptr-&gt;head;
  do {
    cur_name=next_name; next_name=blink[cur_name-name_dir];
    cur_byte=cur_name-&gt;byte_start+cur_depth;
    if (cur_byte==(cur_name+1)-&gt;byte_start) c=0; /* hit end of the name */
    else {
      c=(eight_bits) *cur_byte;
      if (xisupper(c)) c=tolower(c);
    }
  blink[cur_name-name_dir]=bucket[c]; bucket[c]=cur_name;
  } while (next_name);
  --sort_ptr; unbucket(cur_depth+1);
}
@ @&lt;Output index...@&gt;= {
  cur_name=sort_ptr-&gt;head;
  do {
    out_str("\\I");
@.\\I@&gt;
    @&lt;Output the name at |cur_name|@&gt;;
    @&lt;Output the cross-references at |cur_name|@&gt;;
    cur_name=blink[cur_name-name_dir];
  } while (cur_name);
  --sort_ptr;
}
@ @&lt;Output the name...@&gt;=
switch (cur_name-&gt;ilk) {
  case normal: case func_template: if (is_tiny(cur_name)) out_str("\\|");
    else {char *j;
      for (j=cur_name-&gt;byte_start;j&lt;(cur_name+1)-&gt;byte_start;j++)
        if (xislower(*j)) goto lowcase;
      out_str("\\."); break;
lowcase: out_str("\\\\");
    }
  break;
@.\\|@&gt;
@.\\.@&gt;
@.\\\\@&gt;
  case wildcard: out_str("\\9");@+ goto not_an_identifier;
@.\\9@&gt;
  case typewriter: out_str("\\.");
@.\\.@&gt;
  case roman: not_an_identifier: out_name(cur_name,0); goto name_done;
  case custom: {char *j; out_str("$\\");
    for (j=cur_name-&gt;byte_start;j&lt;(cur_name+1)-&gt;byte_start;j++)
      out(*j=='_'? 'x': *j=='$'? 'X': *j);
    out('$');
    goto name_done;
    }
  default: out_str("\\&amp;");
@.\\\&amp;@&gt;
}
out_name(cur_name,1);
name_done:@;
@ Section numbers that are to be underlined are enclosed in `\.{\\[}$\,\ldots\,$\.]'.

@&lt;Output the cross-references at |cur_name|@&gt;=
@&lt;Invert the cross-reference list at |cur_name|, making |cur_xref| the head@&gt;;
do {
  out_str(", "); cur_val=cur_xref-&gt;num;
  if (cur_val&lt;def_flag) out_section(cur_val);
  else {out_str("\\["); out_section(cur_val-def_flag); out(']');}
@.\\[@&gt;
  cur_xref=cur_xref-&gt;xlink;
} while (cur_xref!=xmem);
out('.'); finish_line();
@ List inversion is best thought of as popping elements off one stack and pushing them onto another. In this case |cur_xref| will be the head of the stack that we push things onto.

@&lt;Global variables@&gt;=
xref_pointer next_xref, this_xref;
  /* pointer variables for rearranging a list */

@ @&lt;Invert the cross-reference list at |cur_name|, making |cur_xref| the head@&gt;=
this_xref=(xref_pointer)cur_name-&gt;xref; cur_xref=xmem;
do {
  next_xref=this_xref-&gt;xlink; this_xref-&gt;xlink=cur_xref;
  cur_xref=this_xref; this_xref=next_xref;
} while (this_xref!=xmem);
@ @&lt;Output all the section names@&gt;=section_print(root)
@ Procedure |unbucket| goes through the buckets and adds nonempty lists to the stack, using the collating sequence specified in the |collate| array. The parameter to |unbucket| tells the current depth in the buckets. Any two sequences that agree in their first 255 character positions are regarded as identical.

@d infinity 255 /* $\infty$ (approximately) */

@&lt;Predeclaration of procedures@&gt;=
void  unbucket();

@ @c
void
unbucket(d) /* empties buckets having depth |d| */
eight_bits d;
{
  int c;  /* index into |bucket|; cannot be a simple |char| because of sign
    comparison below*/
  for (c=100+128; c&gt;= 0; c--) if (bucket[collate[c]]) {
@^high-bit character handling@&gt;
    if (sort_ptr&gt;=scrap_info_end) overflow("sorting");
    sort_ptr++;
    if (sort_ptr&gt;max_sort_ptr) max_sort_ptr=sort_ptr;
    if (c==0) sort_ptr-&gt;depth=infinity;
    else sort_ptr-&gt;depth=d;
    sort_ptr-&gt;head=bucket[collate[c]]; bucket[collate[c]]=NULL;
  }
}
@ The following recursive procedure walks through the tree of section names and prints them. @^recursion@&gt;

@&lt;Predeclaration of procedures@&gt;=
void section_print();

@ @c
void
section_print(p) /* print all section names in subtree |p| */
name_pointer p;
{
  if (p) {
    section_print(p-&gt;llink); out_str("\\I");
@.\\I@&gt;
    tok_ptr=tok_mem+1; text_ptr=tok_start+1; scrap_ptr=scrap_info; init_stack;
    app(p-name_dir+section_flag); make_output();
    footnote(cite_flag);
    footnote(0); /* |cur_xref| was set by |make_output| */
    finish_line();@/
    section_print(p-&gt;rlink);
  }
}
@ Because on some systems the difference between two pointers is a |long| rather than an |int|, we use \.{\%ld} to print these quantities.

@c
void
print_stats() {
  printf("\nMemory usage statistics:\n");
@.Memory usage statistics:@&gt;
  printf("%ld names (out of %ld)\n",
            (long)(name_ptr-name_dir),(long)max_names);
  printf("%ld cross-references (out of %ld)\n",
            (long)(xref_ptr-xmem),(long)max_refs);
  printf("%ld bytes (out of %ld)\n",
            (long)(byte_ptr-byte_mem),(long)max_bytes);
  printf("Parsing:\n");
  printf("%ld scraps (out of %ld)\n",
            (long)(max_scr_ptr-scrap_info),(long)max_scraps);
  printf("%ld texts (out of %ld)\n",
            (long)(max_text_ptr-tok_start),(long)max_texts);
  printf("%ld tokens (out of %ld)\n",
            (long)(max_tok_ptr-tok_mem),(long)max_toks);
  printf("%ld levels (out of %ld)\n",
            (long)(max_stack_ptr-stack),(long)stack_size);
  printf("Sorting:\n");
  printf("%ld levels (out of %ld)\n",
            (long)(max_sort_ptr-scrap_info),(long)max_scraps);
}@** Index.
If you have read and understood the code for Phase III above, you know what
is in this index and how it got here. All sections in which an identifier is
used are listed with that identifier, except that reserved words are
indexed only when they appear in format definitions, and the appearances
of identifiers in section names are not indexed. Underlined entries
correspond to where the identifier was declared. Error messages, control
sequences put into the output, and a few
other things like ``recursion'' are indexed here too.
</t>
<t tx="ekr.20170415084531.1"># lowercase xml tags, one per line.

html
body
head
div
table
nodeA
nodeB
</t>
<t tx="ekr.20170504122245.1"></t>
<t tx="ekr.20170504122721.1">def at_auto_child():
    pass
</t>
<t tx="ekr.20170504122721.10">def class2_method1():
    pass
</t>
<t tx="ekr.20170504122721.11">def class2_method2():
    pass
</t>
<t tx="ekr.20170504122721.12">This would be the text in this level one node.

And this.
</t>
<t tx="ekr.20170504122721.13">Another one
</t>
<t tx="ekr.20170504122721.14">See what we did there - one more '#' - this is a subnode.
</t>
<t tx="ekr.20170504122721.15">Sec 1.
</t>
<t tx="ekr.20170504122721.16">Sec 2.
</t>
<t tx="ekr.20170504122721.17">Sec 1.
</t>
<t tx="ekr.20170504122721.18">Sec 2.
</t>
<t tx="ekr.20170504122721.2">Sec 1.

</t>
<t tx="ekr.20170504122721.3">Sec 2.

</t>
<t tx="ekr.20170504122721.4">def spam():
    pass
</t>
<t tx="ekr.20170504122721.5">def eggs():
    pass


</t>
<t tx="ekr.20170504122721.6">class class1:
    @others
</t>
<t tx="ekr.20170504122721.7">def class1_method1():
    pass
</t>
<t tx="ekr.20170504122721.8">def class1_method2():
    pass
# After @others in child1.
</t>
<t tx="ekr.20170504122721.9">class class2:
    @others
# last line
</t>
<t tx="ekr.20170701153730.1"></t>
<t tx="ekr.20170712045534.1"></t>
<t tx="ekr.20170712045644.1">======================================================================
ERROR: runTest (leo.core.leoTest.GeneralTestCase)
@test x.makeShadowDirectory
@test all commands have an event arg

@test add/delete html comments

----------------------------------------------------------------------
Traceback (most recent call last):
  File "/mnt/usr1/usr1/home/tbrown/t/Package/leo/git/leo-editor/leo/core/leoTest.py", line 211, in runTest
    builtins.execfile(scriptFile, d)
  File "/home/tbrown/.leo/scriptFile.py", line 27, in &lt;module&gt;
    assert p.b == s,'fail5: s\n%s\nresult\n%s' % (repr(s),repr(p.b))
AssertionError: fail5: s
u'@language html\n&lt;html&gt;\n    text \n&lt;/html&gt;\n'
result
u'@language html\n&lt;html&gt;\n   text \n&lt;/html&gt;\n'

======================================================================
FAIL: runTest (leo.core.leoTest.EditBodyTestCase)
EditBodyTestCase: addComments
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/mnt/usr1/usr1/home/tbrown/t/Package/leo/git/leo-editor/leo/core/leoTest.py", line 105, in runTest
    self.editBody()
  File "/mnt/usr1/usr1/home/tbrown/t/Package/leo/git/leo-editor/leo/core/leoTest.py", line 85, in editBody
    compareHeadlines=False), '%s: before undo1' % commandName
AssertionError: addComments: before undo1</t>
<t tx="ekr.20170712053944.1"></t>
<t tx="ekr.20170712132824.1"></t>
<t tx="ekr.20170712132900.1">w = c.frame.body.wrapper
p = g.findNodeInTree(c,p,'html')
assert p,'not found: html'
old_indent = c.config.getBool('indent_added_comments',default=True)
table = (
    (
        False,
        '@language html\n&lt;html&gt;\n&lt;!-- text --&gt;\n&lt;/html&gt;\n',
        '@language html\n&lt;html&gt;\ntext\n&lt;/html&gt;\n',
    ),
    (
        True,
        '@language html\n&lt;html&gt;\n    &lt;!-- text --&gt;\n&lt;/html&gt;\n',
        '@language html\n&lt;html&gt;\n    text\n&lt;/html&gt;\n',
    ),
)
try:
    for indent, s1, expected in table:
        # Step 1: set the setting.
        c.config.set(None, 'bool', 'indent_added_comments', indent, warn=False)
        val = c.config.getBool('indent_added_comments')
        assert indent == val, (repr(indent), repr(val))
        # Step 2: set p.b and the insert point.
        c.selectPosition(p)
        p.b = s1
        i = p.b.find('text')
        assert i &gt; -1,'fail1: %s' % (repr(p.b))
        w.setSelectionRange(i,i+4)
        # Step 3: test delete-comments
        c.deleteComments()
        assert p.b == expected, ('indent: %5s got:\n%r\nexpected:\n%r' % (indent, p.b, expected))
finally:
    c.config.set(p, 'bool', 'indent_added_comments', old_indent)
    val = c.config.getBool('indent_added_comments')
    assert old_indent == val, (repr(indent), repr(val))
</t>
<t tx="ekr.20170712132933.1">@language html
</t>
<t tx="ekr.20170712132933.2">@language html
&lt;html&gt;
    text
&lt;/html&gt;
</t>
<t tx="ekr.20170712134334.1">w = c.frame.body.wrapper
p = g.findNodeInTree(c,p,'python')
assert p,'not found: python'
old_indent = c.config.getBool('indent_added_comments',default=True)
table = (
    (
        True,
        '@language python\ndef spam():\n    pass\n\n# after',
        '@language python\ndef spam():\n    # pass\n\n# after',
    ),
    (
        False,
        '@language python\ndef spam():\n    pass\n\n# after',
        '@language python\ndef spam():\n#     pass\n\n# after',
    ),
)
try:
    for indent, s1, expected in table:
        # Step 1: set the setting.
        c.config.set(None, 'bool', 'indent_added_comments', indent, warn=False)
        val = c.config.getBool('indent_added_comments')
        assert indent == val, (repr(indent), repr(val))
        # Step 2: set p.b and the insert point.
        c.selectPosition(p)
        p.b = s1
        i = p.b.find('pass')
        assert i &gt; -1,'fail1: %s' % (repr(p.b))
        w.setSelectionRange(i,i+4)
        # Step 3: test add-comments
        c.addComments()
        assert p.b == expected, ('indent: %5s got:\n%r\nexpected:\n%r' % (indent, p.b, expected))
finally:
    c.config.set(p, 'bool', 'indent_added_comments', old_indent)
    val = c.config.getBool('indent_added_comments')
    assert old_indent == val, (repr(indent), repr(val))
</t>
<t tx="ekr.20170712134334.2">@language python
def spam():
#     pass

# after
</t>
<t tx="ekr.20170712134948.1">w = c.frame.body.wrapper
p = g.findNodeInTree(c,p,'python')
assert p,'not found: python'
old_indent = c.config.getBool('indent_added_comments',default=True)
table = (
    (
        True,
        '@language python\ndef spam():\n    # pass\n\n# after',
        '@language python\ndef spam():\n    pass\n\n# after',
    ),
    (
        False,
        '@language python\ndef spam():\n#     pass\n\n# after',
        '@language python\ndef spam():\n    pass\n\n# after',
    ),
)
try:
    for indent, s1, expected in table:
        # Step 1: set the setting.
        c.config.set(None, 'bool', 'indent_added_comments', indent, warn=False)
        val = c.config.getBool('indent_added_comments')
        assert indent == val, (repr(indent), repr(val))
        # Step 2: set p.b and the insert point.
        c.selectPosition(p)
        p.b = s1
        i = p.b.find('pass')
        assert i &gt; -1,'fail1: %s' % (repr(p.b))
        w.setSelectionRange(i,i+4)
        # Step 3: test delete-comments
        c.deleteComments()
        assert p.b == expected, ('indent: %5s got:\n%r\nexpected:\n%r' % (indent, p.b, expected))
finally:
    c.config.set(p, 'bool', 'indent_added_comments', old_indent)
    val = c.config.getBool('indent_added_comments')
    assert old_indent == val, (repr(indent), repr(val))
</t>
<t tx="ekr.20170712134948.3">@language python
def spam():
    pass

# after
</t>
<t tx="ekr.20170712135224.1">w = c.frame.body.wrapper
p = g.findNodeInTree(c,p,'rest and python')
assert p,'not found: rest and python'
old_indent = c.config.getBool('indent_added_comments',default=True)
table = (
    (
        False,
        '@language rest\nrest text.\n@language python\ndef spam():\n#     pass\n# after',
        '@language rest\nrest text.\n@language python\ndef spam():\n    pass\n# after',
    ),
    (
        True,
        '@language rest\nrest text.\n@language python\ndef spam():\n    # pass\n# after',
        '@language rest\nrest text.\n@language python\ndef spam():\n    pass\n# after',
    ),
)
try:
    for indent, s1, expected in table:
        # Step 1: set the setting.
        c.config.set(None, 'bool', 'indent_added_comments', indent, warn=False)
        val = c.config.getBool('indent_added_comments')
        assert indent == val, (repr(indent), repr(val))
        # Step 2: set p.b and the insert point.
        c.selectPosition(p)
        p.b = s1
        i = p.b.find('pass')
        assert i &gt; -1,'fail1: %s' % (repr(p.b))
        w.setSelectionRange(i,i+4)
        # Step 3: test add-comments
        c.deleteComments()
        assert p.b == expected, ('indent: %5s got:\n%r\nexpected:\n%r' % (indent, p.b, expected))
finally:
    c.config.set(p, 'bool', 'indent_added_comments', old_indent)
    val = c.config.getBool('indent_added_comments')
    assert old_indent == val, (repr(indent), repr(val))
</t>
<t tx="ekr.20170712135224.2">@language rest
rest text.
@language python
def spam():
    pass
# after
</t>
<t tx="ekr.20170712140759.1">@language rest
@wrap

I'm also having 3 failed tests with current master, some of those seem to be different from Terry's ones:

Trying to create a QVariant instance of QMetaType::Void type, an invalid QVariant will be constructed instead


======================================================================
FAIL: runTest (leo.core.leoTest.GeneralTestCase)
@test g.app.config @buttons and @commands logic

----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\dev\Leo-5.4\leo\core\leoTest.py", line 209, in runTest
    exec(compile(script, scriptFile, 'exec'), d)
  File "C:\Users\sysadmin\.leo\scriptFile.py", line 26, in &lt;module&gt;
    assert fn in aList,'%s not in unitTestDict[%s]' % (fn,key)
AssertionError: myLeoSettings.leo not in unitTestDict[config.doCommands-file-names]

======================================================================
FAIL: runTest (leo.core.leoTest.GeneralTestCase)
@test unbound Alt-9 key is completely ignored

----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\dev\Leo-5.4\leo\core\leoTest.py", line 209, in runTest
    exec(compile(script, scriptFile, 'exec'), d)
  File "C:\Users\sysadmin\.leo\scriptFile.py", line 27, in &lt;module&gt;
    assert g.app.unitTestDict.get('handleUnboundChar-ignore-alt-or-ctrl')
AssertionError

----------------------------------------------------------------------
Ran 905 tests in 120.272s

FAILED (failures=3, skipped=11)

@language python


</t>
<t tx="ekr.20170818044949.1">gnx: ekr.20090627070131.4975
</t>
<t tx="ekr.20170818044949.2">gnx: ekr.20090627070131.4975
</t>
<t tx="ekr.20170818044949.3">gnx: ekr.20090627070131.4976
unl: spam
gnx: ekr.20090627070131.4977
unl: cheese
</t>
<t tx="ekr.20170818044949.4">gnx: ekr.20100801125533.5788
</t>
<t tx="ekr.20170818044949.5">gnx: ekr.20100801125533.5788
</t>
<t tx="ekr.20170818044949.6">gnx: ekr.20100801125533.5789
unl: spam
gnx: ekr.20100801125533.5790
unl: cheese
</t>
<t tx="ekr.20171126152936.1"># Required for unit tests: See #577.</t>
<t tx="ekr.20171126153044.1"># Required for unit tests: See #577.</t>
<t tx="ekr.20171126153138.1">See #577: https://github.com/leo-editor/leo-editor/issues/577
</t>
<t tx="ekr.20171126172628.1"></t>
<t tx="ekr.20171230052539.1">gnx: ekr.20150626101627.1
</t>
<t tx="ekr.20171230164813.1">gnx: ekr.20150626100719.1
</t>
<t tx="ekr.20171230170021.1">gnx: ekr.20150626101842.1
</t>
<t tx="ekr.20171230171138.1">Header line.

</t>
<t tx="ekr.20171230171343.1">gnx: ekr.20080904084223.1
</t>
<t tx="ekr.20171230171343.12">gnx: ekr.20150626093745.1
</t>
<t tx="ekr.20171230171343.4">gnx: ekr.20090704085350.5056
</t>
<t tx="ekr.20171230171343.6">gnx: ekr.20110610122533.3407
</t>
<t tx="ekr.20171230171343.8">gnx: ekr.20161130052935.1
</t>
<t tx="ekr.20171230171355.20"></t>
<t tx="ekr.20171230171407.1">gnx: ekr.20170504122721.1
unl: at_auto_child
</t>
<t tx="ekr.20171230171407.3">gnx: ekr.20170504122721.4
unl: spam
gnx: ekr.20170504122721.5
unl: eggs
</t>
<t tx="ekr.20171230171407.4">gnx: ekr.20170504122721.6
unl: class class1
gnx: ekr.20170504122721.7
unl: class class1--&gt;class1_method1
gnx: ekr.20170504122721.8
unl: class class1--&gt;class1_method2
gnx: ekr.20170504122721.9
unl: class class2
gnx: ekr.20170504122721.10
unl: class class2--&gt;class2_method1
gnx: ekr.20170504122721.11
unl: class class2--&gt;class2_method2
</t>
<t tx="ekr.20171230171407.5">gnx: ekr.20170504122721.12
unl: A level one node
gnx: ekr.20170504122721.13
unl: Another level one node
gnx: ekr.20170504122721.14
unl: Another level one node--&gt;A level 2 node
</t>
<t tx="ekr.20171230171407.6">gnx: ekr.20170504122721.15
unl: section 1
gnx: ekr.20170504122721.16
unl: section 2
</t>
<t tx="ekr.20171230171407.7">gnx: ekr.20171230171355.20
unl: First line.
gnx: ekr.20170504122721.17
unl: section 1
gnx: ekr.20170504122721.18
unl: section 2
</t>
<t tx="ekr.20171230171407.8"></t>
<t tx="ekr.20171230171614.10">gnx: ekr.20171230171138.1
unl: !Declarations
gnx: ekr.20170504122721.2
unl: section 1
gnx: ekr.20170504122721.3
unl: section 2
</t>
<t tx="ekr.20180214042153.1">False is the legacy value.</t>
</tnodes>
</leo_file>
