Documentation Source Text

Check-in [a4f337e315]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Rework the requirements scanner to operate off of sqlite3.c instead of sqlite3.h. Add the hd_requirement procedure to wrap.tcl.
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: a4f337e3154a733b9a7ec874256c1af243c2ebcb
User & Date: drh 2008-07-22 17:04:32
Context
2008-07-22
17:08
Revert capi3ref.in to scan sqlite3.h instead of sqlite3.c. check-in: 1c87d6c3f1 user: drh tags: trunk
17:04
Rework the requirements scanner to operate off of sqlite3.c instead of sqlite3.h. Add the hd_requirement procedure to wrap.tcl. check-in: a4f337e315 user: drh tags: trunk
11:09
Fix a formatting bug in the system requirements document. check-in: cf1736c6e5 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to pages/capi3ref.in.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

18
19
20
21
22
23
24
25


26
27

28
29
30
31
32
33
34
..
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53



54
55
56
57
58
59
60





61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79




80
81
82





83
84
85
86
87
88
89

90
91
92
93
94
95
96
97
98
99
100
101
102



103
104
105
106




107
108
109
110


111
112
113
114
115
116







117
118
119
120
121
122
123
...
125
126
127
128
129
130
131




132
133
134
135




136
137
138
139
140
141
142
<title>C/C++ Interface For SQLite Version 3</title>

<h2 class=pdf_section>C/C++ Interface For SQLite Version 3</h2>

<tcl>
set in [open sqlite3.h] 
set title {}       ;# title of a section of interface definition
set type {}        ;# one of: constant datatype function
set body {}        ;# human-readable description
set code {}        ;# C code of the definition
set phase 0        ;# Phase used by the parser 
set content {}     ;# List of records, one record per definition
set dcnt 0         ;# Number of individual declarations
set lineno 0       ;# input file line number
set intab 0        ;# In a covenents or limitations table
set inrow 0        ;# In a row of a table
set rowbody {}     ;# Content of a row

unset -nocomplain keyword

# End a table row or the complete table.
#
proc endrow {} {
  global inrow body rowbody
  if {$inrow} {
    append body [string trim $rowbody]</td></tr>\n


    set inrow 0
    set rowbody {}

  }
}
proc endtab {} {
  global intab body
  endrow
  if {$intab} {
    append body "</table>\n"
................................................................................
  global intab body
  endtab
  append body {<table border="0" cellpadding="5" cellspacing="0">}
  append body \n
  set intab 1
}

# Read sqlite3.h line by line and extract interface definition
# information.
#
while {![eof $in]} {
  set line [gets $in]
  incr lineno
  if {$phase==0} {
    # Looking for the CAPI3REF: keyword



    if {[regexp {^\*\* CAPI3REF: +(.*)} $line all tx]} {
      set title $tx
      set title_lineno $lineno
      set phase 1
    }
  } elseif {$phase==1} {
    if {[string range $line 0 1]=="**"} {





      set lx [string trim [string range $line 3 end]]
      if {[regexp {^CATEGORY: +([a-z]*)} $lx all cx]} {
        set type $cx
      } elseif {[regexp {^KEYWORDS: +(.*)} $lx all kx]} {
        foreach k $kx {
          set keyword($k) 1
        }
      } elseif {[regexp {^INVARIANTS:$} $lx]} {
        append body "\n<h3>Invariants:</h3>\n"
        starttab
        set phase 2
      } elseif {[regexp {^LIMITATIONS:$} $lx]} {
        append body "\n<h3>Limitations:</h3>\n"
        starttab
        set phase 2
      } else {
        append body $lx\n
      }
    } elseif {[string range $line 0 1]=="*/"} {




      set phase 3
    }
  } elseif {$phase==2} {





    if {[string range $line 0 1]=="**"} {
      set lx [string trim [string range $line 3 end]]
      if {[regexp {\{([\w.]+)\}\s+(.+)$} $lx all tag lxtail]} {
        endrow
        append body "<tr><td valign=\"top\">$tag</td>\
                     \n<td valign=\"top\">\n"
        set rowbody $lxtail\n

        set inrow 1
      } elseif {[regexp {^INVARIANTS:$} $lx]} {
        endtab
        append body "\n<h3>Invariants:</h3>\n"
        starttab
      } elseif {[regexp {^LIMITATIONS:$} $lx]} {
        endtab
        append body "\n<h3>Limitations:</h3>\n"
        starttab
      } else {
        append rowbody $lx\n
      }
    } elseif {[string range $line 0 1]=="*/"} {



      endtab
      set phase 3
    }
  } elseif {$phase==3} {




    if {$line==""} {
      set kwlist [lsort [array names keyword]]
      unset -nocomplain keyword
      set key $type:$kwlist


      regsub { *\{[\w.]+\}} $title {} title
      regsub -all { *\{[\w.]+\}} $body {} body
      set body [string map \
          {<todo> {<font color="red">(TODO: } </todo> )</font>} $body]
      set code [string map {& &amp; < &lt; > &gt;} $code]
      lappend content [list $key $title $type $kwlist $body $code]







      set title {}
      set keywords {}
      set type {}
      set body {}
      set code {}
      set phase 0
      set dcnt 0
................................................................................
      if {[regexp {^#define (SQLITE_[A-Z0-9_]+)} $line all kx]} {
        set type constant
        set keyword($kx) 1
        incr dcnt
      } elseif {[regexp {^typedef .*(sqlite[0-9a-z_]+);} $line all kx]} {
        set type datatype
        set keyword($kx) 1




        incr dcnt
      } elseif {[regexp {^[a-z].*[ *](sqlite3_[a-z0-9_]+)\(} $line all kx]} {
        set type function
        set keyword($kx) 1




        incr dcnt
      } elseif {[regexp {^SQLITE_EXTERN .*(sqlite[0-9a-z_]+);} $line all kx]} {
        set type datatype
        set keyword($kx) 1
        incr dcnt
      }
      append code $line\n





|











>





|

|
>
>


>







 







|
|





|
>
>
>







>
>
>
>
>











|
|






>
>
>
>



>
>
>
>
>







>





|

|





>
>
>




>
>
>
>




>
>






>
>
>
>
>
>
>







 







>
>
>
>




>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
..
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
...
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
<title>C/C++ Interface For SQLite Version 3</title>

<h2 class=pdf_section>C/C++ Interface For SQLite Version 3</h2>

<tcl>
set in [open sqlite3.c] 
set title {}       ;# title of a section of interface definition
set type {}        ;# one of: constant datatype function
set body {}        ;# human-readable description
set code {}        ;# C code of the definition
set phase 0        ;# Phase used by the parser 
set content {}     ;# List of records, one record per definition
set dcnt 0         ;# Number of individual declarations
set lineno 0       ;# input file line number
set intab 0        ;# In a covenents or limitations table
set inrow 0        ;# In a row of a table
set rowbody {}     ;# Content of a row
set rowtag {}      ;# 
unset -nocomplain keyword

# End a table row or the complete table.
#
proc endrow {} {
  global inrow body rowbody rowtag
  if {$inrow} {
    set rowbody [string trim $rowbody]
    append body $rowbody</td></tr>\n
    hd_requirement $rowtag $rowbody
    set inrow 0
    set rowbody {}
    set rowtag {}
  }
}
proc endtab {} {
  global intab body
  endrow
  if {$intab} {
    append body "</table>\n"
................................................................................
  global intab body
  endtab
  append body {<table border="0" cellpadding="5" cellspacing="0">}
  append body \n
  set intab 1
}

# Read sqlite3.c line by line and extract interface definition
# information (found in what was originally sqlite3.h).
#
while {![eof $in]} {
  set line [gets $in]
  incr lineno
  if {$phase==0} {
    # Looking for the CAPI3REF: keyword.  This marks the beginning of
    # an interface definition.  When the CAPI3REF keywords is seen, 
    # record the interface title and then switch to "phase 1".
    #
    if {[regexp {^\*\* CAPI3REF: +(.*)} $line all tx]} {
      set title $tx
      set title_lineno $lineno
      set phase 1
    }
  } elseif {$phase==1} {
    if {[string range $line 0 1]=="**"} {
      # Record all lines of column following the CAPI3REF keyword as the
      # description of the interface.  Except, look for special keywords
      # CATEGORY, KEYWORDS, INVARIANTS, and ASSUMPTIONS and process them
      # separately.
      #
      set lx [string trim [string range $line 3 end]]
      if {[regexp {^CATEGORY: +([a-z]*)} $lx all cx]} {
        set type $cx
      } elseif {[regexp {^KEYWORDS: +(.*)} $lx all kx]} {
        foreach k $kx {
          set keyword($k) 1
        }
      } elseif {[regexp {^INVARIANTS:$} $lx]} {
        append body "\n<h3>Invariants:</h3>\n"
        starttab
        set phase 2
      } elseif {[regexp {^ASSUMPTIONS:$} $lx]} {
        append body "\n<h3>Assumptions:</h3>\n"
        starttab
        set phase 2
      } else {
        append body $lx\n
      }
    } elseif {[string range $line 0 1]=="*/"} {
      # When we reach the end of the block comment that contained the
      # CAPI3REF keyword, that ends the description.  Switch to phase 3
      # in order to begin picking up the interface definition.
      #
      set phase 3
    }
  } elseif {$phase==2} {
    # In phase 2, we are still reading the description of an interface.
    # But at this point we have seen either an INVARIANTS or ASSUMPTIONS
    # keyword and are thus reading in the specially formatted invariants
    # or assumptions.
    #
    if {[string range $line 0 1]=="**"} {
      set lx [string trim [string range $line 3 end]]
      if {[regexp {\{([\w.]+)\}\s+(.+)$} $lx all tag lxtail]} {
        endrow
        append body "<tr><td valign=\"top\">$tag</td>\
                     \n<td valign=\"top\">\n"
        set rowbody $lxtail\n
        set rowtag $tag
        set inrow 1
      } elseif {[regexp {^INVARIANTS:$} $lx]} {
        endtab
        append body "\n<h3>Invariants:</h3>\n"
        starttab
      } elseif {[regexp {^ASSUMPTIONS:$} $lx]} {
        endtab
        append body "\n<h3>Assumptions:</h3>\n"
        starttab
      } else {
        append rowbody $lx\n
      }
    } elseif {[string range $line 0 1]=="*/"} {
      # At the end of the block comment, switch to phase 3 in order to begin
      # picking up the interface definition.
      #
      endtab
      set phase 3
    }
  } elseif {$phase==3} {
    # Reading in an interface definition.  Stop reading at the first blank
    # line.
    #
    regsub {^SQLITE_API } $line {} line
    if {$line==""} {
      set kwlist [lsort [array names keyword]]
      unset -nocomplain keyword
      set key $type:$kwlist
      set reqtag {}
      regexp {\{(.*)\}} $title all reqtag
      regsub { *\{[\w.]+\}} $title {} title
      regsub -all { *\{[\w.]+\}} $body {} body
      set body [string map \
          {<todo> {<font color="red">(TODO: } </todo> )</font>} $body]
      set code [string map {& &amp; < &lt; > &gt;} $code]
      lappend content [list $key $title $type $kwlist $body $code]
      if {$reqtag!=""} {
        set reqbody "The sqlite3.h header file shall define the\n"
        append reqbody "the following interfaces:\n<blockquote><pre>\n"
        append reqbody $code
        append reqbody "\n</pre></blockquote>"
        hd_requirement $reqtag $reqbody 1
      }
      set title {}
      set keywords {}
      set type {}
      set body {}
      set code {}
      set phase 0
      set dcnt 0
................................................................................
      if {[regexp {^#define (SQLITE_[A-Z0-9_]+)} $line all kx]} {
        set type constant
        set keyword($kx) 1
        incr dcnt
      } elseif {[regexp {^typedef .*(sqlite[0-9a-z_]+);} $line all kx]} {
        set type datatype
        set keyword($kx) 1
        incr dcnt
      } elseif {[regexp {^struct (sqlite3_[0-9a-z_]+)} $line all kx]} {
        set type datatype
        set keyword($kx) 1
        incr dcnt
      } elseif {[regexp {^[a-z].*[ *](sqlite3_[a-z0-9_]+)\(} $line all kx]} {
        set type function
        set keyword($kx) 1
        incr dcnt
      } elseif {[regexp {^[a-z].*[ *](sqlite3_[a-z0-9_]+);} $line all kx]} {
        set type datatype
        set keyword($kx) 1
        incr dcnt
      } elseif {[regexp {^SQLITE_EXTERN .*(sqlite[0-9a-z_]+);} $line all kx]} {
        set type datatype
        set keyword($kx) 1
        incr dcnt
      }
      append code $line\n

Added pages/hlreq.in.



































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<title>High-Level Requirements</title>

<h2>High-Level Requirements for SQLite</h2>

<table cellspacing="20">
<tcl>
foreach id [lsort [array names ALLREQ H*]] {
  hd_puts "<tr><td valign=\"top\">$id</td><td valign=\"top\">"
  if {$ALLREQ_VERBATIM($id)} {
    hd_puts $ALLREQ($id)
  } else {
    hd_resolve $ALLREQ($id)
  }
  hd_puts "</td></tr>"
}
</tcl>
</table>

Deleted pages/requirements.in.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
<title>SQLite Requirements</title>

<p>
This document contains the text of all requirements that define
the operation of SQLite.
</p>

<p>This document is currently a work in progress.  It is incomplete
and inaccurate.  Check back later for further updates.</p>

<h2>Requirements</h2>

<table border="0" cellpadding="5" cellspacing="0">
<tcl>
#############################################################################
# Scan the sqlite3.h header file and extract requirements information.
#
set in [open sqlite3.h] 
set type {}        ;# one of: constant datatype function
set code {}        ;# C code of the definition
set phase 0        ;# Phase used by the parser 
set content {}     ;# List of records, one record per definition
set dcnt 0         ;# Number of individual declarations
set lineno 0       ;# input file line number
set inreq 0        ;# True if in a requirement definition
set reqbody {}     ;# Content of a requirement definition
unset -nocomplain req

# Record the end of a requirement stored in reqtag and reqbody
#
proc end_req {} {
  global inreq reqtag reqbody req reqtype
  if {$inreq} {
    if {[info exists req($reqtag)]} {
      puts stderr "duplicate requirement label: $reqtag"
    }
    set req($reqtag) [string trim $reqbody]
    set reqtype($reqtag) text
    set inreq 0
    set rowbody {}
  }
}

# Read sqlite3.h line by line and extract interface definition
# information.
#
while {![eof $in]} {
  set line [gets $in]
  incr lineno
  if {$phase==0} {
    # Looking for the CAPI3REF: keyword
    if {[regexp {^\*\* CAPI3REF: +(.*)} $line all tx]} {
      set title $tx
      set title_lineno $lineno
      set phase 1
    }
  } elseif {$phase==1} {
    if {[string range $line 0 1]=="**"} {
      set lx [string trim [string range $line 3 end]]
      if {[regexp {^INVARIANTS:$} $lx]} {
        set phase 2
      } elseif {[regexp {^LIMITATIONS:$} $lx]} {
        set phase 2
      }
    } elseif {[string range $line 0 1]=="*/"} {
      set phase 3
    }
  } elseif {$phase==2} {
    if {[string range $line 0 1]=="**"} {
      set lx [string trim [string range $line 3 end]]
      if {[regexp {\{([\w.]+)\}\s+(.+)$} $lx all tag lxtail]} {
        end_req
        set reqtag $tag
        set reqbody $lxtail\n
        set inreq 1
      } elseif {[regexp {^INVARIANTS:$} $lx]} {
        end_req
      } elseif {[regexp {^LIMITATIONS:$} $lx]} {
        end_req
      } else {
        append reqbody $lx\n
      }
    } elseif {[string range $line 0 1]=="*/"} {
      end_req
      set phase 3
    }
  } elseif {$phase==3} {
    if {$line==""} {
      if {[regexp {\{(.+)\}} $title all reqtag]} {
        if {[info exists req($reqtag)]} {
          puts stderr "duplicate requirement label: $reqtag"
        }
        set req($reqtag) [string map {& &amp; < &lt; > &gt;} $code]
        set reqtype($reqtag) code
      }
      set title {}
      set type {}
      set code {}
      set phase 0
      set dcnt 0
    } else {
      if {[regexp {^#define (SQLITE_[A-Z0-9_]+)} $line all kx]} {
        set type constant
        set keyword($kx) 1
        incr dcnt
      } elseif {[regexp {^typedef .*(sqlite[0-9a-z_]+);} $line all kx]} {
        set type datatype
        set keyword($kx) 1
        incr dcnt
      } elseif {[regexp {^[a-z].*[ *](sqlite3_[a-z0-9_]+)\(} $line all kx]} {
        set type function
        set keyword($kx) 1
        incr dcnt
      } elseif {[regexp {^SQLITE_EXTERN .*(sqlite[0-9a-z_]+);} $line all kx]} {
        set type datatype
        set keyword($kx) 1
        incr dcnt
      }
      append code $line\n
    }
  }
}
close $in
# End of requirements gathering from sqlite3.h
##############################################################################

# Todo:  add additional requirements gathering here.

##############################################################################
# Render the requirements
#
foreach reqtag [lsort [array names req]] {
  set reqbody $req($reqtag)
  hd_puts "<tr><td valign=\"top\">$reqtag</td><td valign=\"top\">"
  if {$reqtype($reqtag)=="code"} {
    hd_puts "The sqlite3.h header file defines the following interfaces:"
    hd_puts "<blockquote><pre>"
    hd_puts $reqbody
    hd_puts "</pre></blockquote>"
  } else {
    hd_resolve $reqbody
  }
  hd_puts "</td></tr>"
}
</tcl>
</table>
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




































































































































































































































































































Changes to wrap.tcl.

394
395
396
397
398
399
400














401
402
403
404
405
406
407
...
424
425
426
427
428
429
430

431
432
433
434
435
436
437
    regsub -all {RP} $body {</font></b>)<b><font color="#2c2cf0">} body
    ## Place the left-hand side of the rule in the 2nd table column.
    hd_puts "<td><b><font color=\"#2c2cf0\">$body</font></b></td></tr>"
  }
  hd_puts {</table>}
}
















# First pass.  Process all files.  But do not render hyperlinks.
# Merely collect keyword information so that hyperlinks can be
# correctly rendered on the second pass.
#
foreach infile [lrange $argv 3 end] {
  cd $HOMEDIR
................................................................................

# Second pass.  Process all files again.  This time render hyperlinks
# according to the keyword information collected on the first pass.
#
proc hd_keywords {args} {}
rename hd_resolve {}
rename hd_resolve_2ndpass hd_resolve

foreach infile [lrange $argv 3 end] {
  cd $HOMEDIR
  puts "Processing $infile"
  set fd [open $infile r]
  set in [read $fd]
  close $fd
  set title {No Title}







>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>







394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
...
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
    regsub -all {RP} $body {</font></b>)<b><font color="#2c2cf0">} body
    ## Place the left-hand side of the rule in the 2nd table column.
    hd_puts "<td><b><font color=\"#2c2cf0\">$body</font></b></td></tr>"
  }
  hd_puts {</table>}
}

# Record a requirement.  This procedure is active only for the first
# pass.  This procedure becomes a no-op for the second pass.  During
# the second pass, requirements listing report generators can use the
# data accumulated during the first pass to construct their reports.
#
proc hd_requirement {id text {verbatim 0}} {
  global ALLREQ ALLREQ_VERBATIM
  if {[info exists ALLREQ($id)]} {
    puts stderr "duplicate requirement label: $id"
  }
  set ALLREQ($id) $text
  set ALLREQ_VERBATIM($id) $verbatim
}


# First pass.  Process all files.  But do not render hyperlinks.
# Merely collect keyword information so that hyperlinks can be
# correctly rendered on the second pass.
#
foreach infile [lrange $argv 3 end] {
  cd $HOMEDIR
................................................................................

# Second pass.  Process all files again.  This time render hyperlinks
# according to the keyword information collected on the first pass.
#
proc hd_keywords {args} {}
rename hd_resolve {}
rename hd_resolve_2ndpass hd_resolve
proc hd_requirement {args} {}
foreach infile [lrange $argv 3 end] {
  cd $HOMEDIR
  puts "Processing $infile"
  set fd [open $infile r]
  set in [read $fd]
  close $fd
  set title {No Title}