Procházet zdrojové kódy

Added makemd sources.
makemd generates .md from source, used for the website generation.

woollybah před 6 roky
rodič
revize
0a0d305a83
8 změnil soubory, kde provedl 1024 přidání a 0 odebrání
  1. 2 0
      .gitignore
  2. 217 0
      src/makemd/bbdoc.bmx
  3. 70 0
      src/makemd/docnode.bmx
  4. 235 0
      src/makemd/docstyle.bmx
  5. 294 0
      src/makemd/makemd.bmx
  6. binární
      src/makemd/makemd.exe
  7. 137 0
      src/makemd/mdstyle.bmx
  8. 69 0
      src/makemd/parse.bmx

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+*.bak
+.bmx

+ 217 - 0
src/makemd/bbdoc.bmx

@@ -0,0 +1,217 @@
+
+Strict
+
+Import "parse.bmx"
+
+'still pretty ugly - could probably be done in a few lines with Bah.RegEx!
+
+Type TBBLinkResolver
+
+	Method ResolveLink$( link$ ) Abstract
+
+End Type
+
+Private
+
+'finds a 'span' style tag starting at index i - eg: @bold or @{this is bold}
+'
+'returns bit after tag, fills in b and e with begin and end of range.
+Function FindBBTag$( Text$,tag$,i,b Var,e Var )
+	Repeat
+		i=Text.Find( tag,i )
+		If i=-1 Return
+		If Text[i + 1] = Asc(tag) Or Text[i+1] = 32 Then ' ignore tag sequences
+			i :+ 6
+			Continue
+		End If
+		If i=0 Or Text[i-1]<=32 Or Text[i-1]=Asc(">") Exit
+		i:+tag.length
+	Forever
+	b=i
+	i:+1
+	If i=Text.length Return
+	Local t$
+	If Text[i]=Asc("{")
+		i:+1
+		While i<Text.length And Text[i]<>Asc("}")
+			i:+1
+		Wend
+		t=Text[b+2..i]
+		If i<Text.length i:+1
+		e=i
+	Else
+		i:+1
+		While i<Text.length And (IsIdentChar(Text[i]) Or Text[i]=Asc("."))
+			i:+1
+		Wend
+		If Text[i-1]=Asc(".") i:-1
+		t=Text[b+1..i]		
+		e=i
+	EndIf
+	Return t
+End Function
+
+'does simple html tags, bold, italic etc.	
+Function FormatBBTags( Text$ Var,bbTag$,htmlTag$ )
+	Local i
+	Repeat
+		Local b,e
+		Local t$=FindBBTag( Text,bbTag,i,b,e )
+		If Not t Return
+		
+		t="<"+htmlTag+">"+t+"</"+htmlTag+">"
+		Text=Text[..b]+t+Text[e..]
+		i=b+t.length
+	Forever
+End Function
+
+Public
+
+Function BBToHtml2$( Text$,doc:TBBLinkResolver )
+	Local i
+	
+	'headings
+	i=0
+	Local hl=1
+	Repeat
+		i=Text.Find( "~n+",i )
+		If i=-1 Exit
+		
+		Local i2=Text.Find( "~n",i+2 )
+		If i2=-1 Exit
+		
+		Local q$=Text[i+2..i2]
+		q="<h"+hl+">"+q+"</h"+hl+">"
+		
+		If hl=1 hl=2
+		
+		Text=Text[..i]+q+Text[i2..]
+		i:+q.length
+	Forever
+	
+	'tables
+	i=0
+	Repeat
+		i=Text.Find( "~n[",i )
+		If i=-1 Exit
+		
+		Local i2=Text.Find( "~n]",i+2 )
+		If i2=-1 Exit
+		
+		Local q$=Text[i+2..i2]
+		
+		If q.Find( " | " )=-1	'list?
+			'q=q.Replace( "~n*","<li>" )
+			'q="<ul>"+q+"</ul>"
+		Else
+			q=q.Replace( "~n*","</td></tr><tr><td> " )
+			q=q.Replace( " | ","</td><td>" )
+			q="~n<table><tr><td>"+q+"</table>~n"
+		EndIf
+		
+		Text=Text[..i]+q+Text[i2+2..]
+		i:+q.length
+	Forever
+	
+	'quotes
+	i=0
+	Repeat
+		i=Text.Find( "~n{",i )
+		If i=-1 Exit
+		
+		Local i2=Text.Find(  "~n}",i+2 )
+		If i2=-1 Exit
+		
+		Local q$=Text[i+2..i2]
+		
+		q="<blockquote>"+q+"</blockquote>"
+		
+		Text=Text[..i]+q+Text[i2+2..]
+		i:+q.length
+	Forever
+	
+	' images
+	i = 0
+	Repeat
+		i = Text.Find("<img src=", i)
+		If i = -1 Exit
+
+		Local i2:Int = Text.Find(">", i)
+		
+		Local s1:Int = Text.Find("~q", i)
+		Local s2:Int = Text.Find("~q", s1 + 2)
+		
+		If s1 > 0 And s2 > 0 Then
+			Local q:String = "![](assets/"+ Text[s1 + 1..s2] + ")"
+			Text = Text[..i] + q + Text[i2 + 1..]
+			i :+ q.length
+		Else
+			Exit
+		End If
+	Forever
+	
+	'links
+	i=0
+	Repeat
+		Local b,e
+		Local t$=FindBBTag( Text,"#",i,b,e )
+		If Not t Exit
+		
+		t=doc.ResolveLink( t )
+		Text=Text[..b]+t+Text[e..]
+		i=b+t.length
+	Forever
+	
+	'span tags
+	FormatBBTags Text,"@","b"
+
+	FormatBBTags Text,"%","i"
+
+	'escapes
+	i=0
+	Repeat
+		i=Text.Find( "~~",i )
+		If i=-1 Or i=Text.length-1 Exit
+		
+		Local r$=Chr( Text[i+1] )
+		Select r
+		Case "<" r="&lt;"
+		Case ">" r="&gt;"
+		End Select
+		
+		Text=Text[..i]+r+Text[i+2..]
+		i:+r.length
+	Forever
+	
+	Return Text
+	
+End Function
+
+'bbdoc to html conversion
+Function BBToHtml$( Text$,doc:TBBLinkResolver )
+	
+	Text=Text.Replace( "~r~n","~n" )
+	
+	Local out$,i
+	
+	'preformatted code...
+	Repeat
+		Local i1=Text.Find( "~n{{",i )
+		If i1=-1 Exit
+
+		Local i2=Text.Find(  "~n}}",i+3 )
+		If i2=-1 Exit
+		
+		out:+BBToHtml2( Text[i..i1],doc )
+		
+		out:+"~n```~n"+Text[i1+3..i2].Trim()+"~n````~n"
+		
+		i=i2+3
+	Forever
+	
+	out:+BBToHtml2( Text[i..],doc )
+	
+	Return out
+	
+End Function	
+		

+ 70 - 0
src/makemd/docnode.bmx

@@ -0,0 +1,70 @@
+
+Strict
+
+Import BRL.Map
+
+Type TDocNode
+
+	Field id$			'eg: BRL.Audio
+	Field path$		'eg: Modules/Audio/Audio"
+	Field kind$		'eg: "Module", "Function", "Type" etc
+	
+	Field proto$		'eg: Function LoadImage(...)
+	Field protoId:String
+	Field bbdoc$		'eg: Load an image (shortdesc?)
+	Field returns$	'eg: A new image
+	Field about$		'eg: blah etc blah (longdesc?)
+	Field params:TList	'eg: [x - the x coord, y - the y coord]
+	
+	Field docDir$		'eg: ../mod/brl.mod/max2d.mod/doc
+	Field example$	'eg: LoadImage.bmx (path)
+	
+	Field children:TList=New TList
+	
+	Function ForPath:TDocNode( path$ )
+
+		Return TDocNode( _pathMap.ValueForKey( path ) )
+
+	End Function
+	
+	Function Create:TDocNode( id$,path$,kind$ )
+	
+		Local t:TDocNode=TDocNode( _pathMap.ValueForKey( path ) )
+		
+		If t
+			If t.kind<>"/" And t.path<>path Throw "ERROR: "+t.kind+" "+kind
+			If t.path = path Return t
+		Else
+			t=New TDocNode
+			_pathMap.Insert path,t
+		EndIf
+
+		t.id=id
+		t.path=path
+		t.kind=kind
+		
+		Local q:TDocNode=t
+		
+		Repeat
+			If path="/" Exit
+			path=ExtractDir( path )
+			Local p:TDocNode=TDocNode( _pathMap.ValueForKey( path ) )
+			If p
+				p.children.AddLast q
+				Exit
+			EndIf
+			p=New TDocNode
+			p.id=StripDir(path)
+			p.path=path
+			p.kind="/"
+			p.children.AddLast q
+			_pathMap.Insert path,p
+			q=p
+		Forever
+		
+		Return t
+	End Function
+	
+	Global _pathMap:TMap=New TMap
+
+End Type

+ 235 - 0
src/makemd/docstyle.bmx

@@ -0,0 +1,235 @@
+
+Strict
+
+Import BRL.MaxUtil
+Import brl.stringbuilder
+
+Import "bbdoc.bmx"
+
+Import "docnode.bmx"
+
+Global BmxDocDir$=BlitzMaxPath()+"/docs/md"
+
+Global NodeKinds$[]=[ "/","Module","Type" ]
+
+Global LeafKinds$[]=[ "Const","Field","Global","Method","Function","Keyword" ]
+
+Global AllKinds$[]=NodeKinds+LeafKinds
+
+Type TDocStyle Extends TBBLinkResolver
+
+	Field generated:String
+	Field doc:TDocNode
+	Field stack:TList = New TList
+	Field children:TMap
+	Field docURL$
+	Field absDocDir$		'where output doc goes
+	Field relRootDir$		'relative path to root doc dir
+	Field outputPath:String
+	
+	'Field stream:TStream
+	Field indent:Int = 0
+	Field sb:TStringBuilder = New TStringBuilder(16384)
+	
+	Global commands:TMap=New TMap
+
+	Method NodeIsleaf( node:TDocNode )
+		For Local t$=EachIn LeafKinds
+			If t=node.kind Return True
+		Next
+	End Method
+	
+	Method FindNode:TDocNode( node:TDocNode,id$ )
+
+		If node.id.ToLower()=id Then
+			Return node
+		End If
+
+		If node.path.Tolower().EndsWith( "/"+id ) Return node
+
+		For Local t:TDocNode=EachIn node.children
+			Local p:TDocNode=FindNode( t,id )
+			If p Return p
+		Next
+	End Method
+	
+	Method NodeURL$( node:TDocNode, forLink:Int = False )
+		If node.kind="Topic"
+			Return node.path+".md"
+		Else If NodeIsLeaf( node )
+			Local path:String = node.path.ToLower()
+
+			Return ExtractDir( path )+"/#"+node.protoId
+		Else If node.path<>"/"
+			If node.kind = "Module" Then
+				Return node.path.Replace(".", "_")+".md"
+			Else
+				If forLink Then
+					Return node.path.ToLower()'.Replace(".", "_")+".md"
+				Else
+					Return node.path.ToLower() + ".md" '.Replace(".", "_")+".md"
+				End If
+			End If
+		Else
+			Return "/" + node.id + ".md"
+		EndIf
+	End Method
+	
+	Method ResolveLink$( link$ )
+
+		Local id$=link.ToLower().Trim()
+
+		Local node:TDocNode=FindNode( doc,id )
+		
+		If Not node 
+			node=FindNode( TDocNode.ForPath( "/" ),id )
+		EndIf
+		
+		If Not node 
+			Print "Error: Unable to resolve link '"+link+"'"
+			Return link
+		EndIf
+
+		Local url$=nodeURL( node, True )
+
+		'optimize links...
+		If url.StartsWith( docURL+"#" )
+			url=url[ docURL.length.. ]
+		Else If url.StartsWith( doc.path+"/" )
+
+			If doc.kind = "Module" Then
+				url = "../.." + url
+			Else If doc.kind = "Type" Then
+				url = "../../.." + url
+			End If
+		Else
+			url=relRootDir+url
+		EndIf
+		Return "[" + link + "](" + url + ")"
+
+	End Method
+	
+	Method EmitDoc( node:TDocNode)
+		
+		Print "Building: "+node.id
+
+		generated=""
+		doc=node
+		children=New TMap
+		stack.AddLast(children)
+		docURL=NodeURL( doc )
+		absDocDir=BmxDocDir+ExtractDir( docURL )
+
+		If doc.kind = "Type" Then
+			relRootDir="../../.."
+		Else
+			relRootDir="../.."
+		End If
+
+		Local p$=ExtractDir( docURL )
+		While p<>"/"
+			p=ExtractDir( p )
+		Wend
+		If Not relRootDir relRootDir="."
+
+		CreateDir absDocDir,True
+			
+		outputPath = BmxDocDir+docURL
+		
+		If doc.docDir CopyDir doc.docDir,absDocDir
+
+		Local intro$=absDocDir+"/index.bbdoc"
+		If FileType( intro )<>FILETYPE_FILE intro$=absDocDir+"/intro.bbdoc"
+		If FileType( intro )=FILETYPE_FILE
+			Local t$=LoadText( intro )
+			DeleteFile intro ' remove bbdoc file
+			If t.find( "commands.html" )<>-1
+				Print "Error: Document contains 'commands.html'"
+			EndIf
+			doc.about=t+doc.about
+		EndIf
+
+		For Local t:TDocNode=EachIn doc.children
+
+			Local list:TList=TList( children.ValueForKey( t.kind ) )
+			If Not list
+				list=New TList
+				children.Insert t.kind,list
+			EndIf
+
+			list.AddLast t
+		Next
+		
+		EmitHeader
+		
+		If node.kind = "Module" Then
+			For Local t$=EachIn NodeKinds
+				EmitLinks t
+			Next
+		End If
+		
+		Local leafOrder:String[] = ["Field", "Method", "Function", "Global", "Const", "Keyword"]
+
+		For Local order:String = EachIn leafOrder
+			For Local t$=EachIn LeafKinds
+				If order = t Then
+					EmitDecls node, t
+				End If
+			Next
+		Next
+
+		EmitFooter
+		
+		
+		If node.kind <> "/" Then
+			generated=BBToHtml( sb.ToString(),Self )
+
+			SaveText generated,outputPath
+		End If
+
+		sb.SetLength(0)
+
+		For Local t$=EachIn NodeKinds
+			EmitNodes t
+		Next
+		
+		stack.RemoveLast()
+		children = TMap(stack.Last())
+
+	End Method
+	
+	Method EmitNodes( kind$ )
+		Local list:TList=TList( children.ValueForKey( kind ) )
+		If Not list Return
+		
+		For Local t:TDocNode=EachIn list
+			EmitDoc t
+		Next
+
+	End Method
+	
+	Method Emit( t$ )
+		sb.Append(t).AppendNewLine()
+	End Method
+	
+	Method ChildList:TList( kind$ )
+		Return TList( children.ValueForKey( kind ) )
+	End Method
+	
+	Method Underline:String(Text:String, char:String)
+		Local under:Short[Text.length]
+		For Local i:Int = 0 Until Text.length
+			under[i] = Asc(char)
+		Next
+		Return String.FromShorts(under, Text.length)
+	End Method
+	
+	Method EmitHeader() Abstract
+	
+	Method EmitFooter() Abstract
+	
+	Method EmitLinks( kind$ ) Abstract
+	
+	Method EmitDecls( parent:TDocNode, kind$ ) Abstract
+	
+End Type

+ 294 - 0
src/makemd/makemd.bmx

@@ -0,0 +1,294 @@
+'
+' Generates Markdown formated docs.
+'
+'
+'
+'
+Strict
+
+Framework BRL.Basic
+
+Import "docnode.bmx"
+
+Import "mdstyle.bmx"
+
+Local style:TDocStyle=New TRstStyle
+
+DeleteDir BmxDocDir,True
+
+Local root:TDocNode=TDocNode.Create( "BlitzMax Help","/","/" )
+
+
+DocMods
+
+DocBBDocs "/"
+
+style.EmitDoc TDocNode.ForPath( "/" )
+
+Cleanup BmxDocDir
+
+'*****
+
+Function Cleanup( dir$ )
+	For Local e$=EachIn LoadDir( dir )
+		Local p$=dir+"/"+e
+		Select FileType( p )
+		Case FILETYPE_DIR
+			Cleanup p
+		Case FILETYPE_FILE
+			If ExtractExt( e )="bbdoc"
+				DeleteFile p
+			Else If e.ToLower()="commands.html"
+				DeleteFile p
+			EndIf
+		End Select
+	Next
+End Function
+
+Function DocMods()
+
+	For Local modid$=EachIn EnumModules()
+
+'		If not modid.StartsWith("sdl.") Continue
+		If Not modid.StartsWith( "brl." ) And Not modid.StartsWith( "pub." ) And Not modid.StartsWith("maxgui.") And Not modid.StartsWith("sdl.") Continue
+
+		Local p$=ModuleSource( modid )
+		Try
+			docBmxFile p,""
+		Catch ex$
+			Print "Error:"+ex
+		End Try
+	Next
+
+End Function
+
+Function DocBBDocs( docPath$ )
+
+	Local p$=BmxDocDir+docPath
+	
+	For Local e$=EachIn LoadDir( p )
+
+		Local q$=p+"/"+e
+
+		Select FileType( q )
+		Case FILETYPE_FILE
+			Select ExtractExt( e )
+			Case "bbdoc"
+				Local id$=StripExt( e )
+				If id="index" Or id="intro" Continue
+				
+				Local path$=(docPath+"/"+id).Replace( "//","/" )
+				Local node:TDocNode=TDocNode.Create( id,path,"/" )
+
+				node.about=LoadText( q )
+			End Select
+		Case FILETYPE_DIR
+			DocBBDocs docPath+"/"+e
+		End Select
+	Next
+	
+End Function
+
+Function docBmxFile( filePath$,docPath$ )
+
+	If FileType( filePath )<>FILETYPE_FILE
+		Print "Error: Unable to open '"+filePath+"'"
+		Return
+	EndIf
+
+	Local docDir$=ExtractDir( filePath )+"/doc"
+	If FileType( docDir )<>FILETYPE_DIR docDir=""
+
+	Local inrem,typePath$,section$
+	
+	Local bbdoc$,returns$,about$,keyword$,params:TList
+	
+	Local Text$=LoadText( filepath )
+	
+	For Local line$=EachIn Text.Split( "~n" )
+
+		line=line.Trim()
+		Local tline$=line.ToLower()
+		
+		Local i
+		Local id$=ParseIdent( tline,i )
+		
+		If id="end" id:+ParseIdent( tline,i )
+		
+		If i<tline.length And tline[i]=Asc(":")
+			id:+":"
+			i:+1
+		EndIf
+		
+		If inrem
+		
+			If id="endrem"
+			
+				inrem=False
+				
+			Else If id="bbdoc:"
+			
+				bbdoc=line[i..].Trim()
+				keyword=""
+				returns=""
+				about=""
+				params=Null
+				section="bbdoc"
+
+			Else If bbdoc 
+			
+				Select id
+				Case "keyword:"
+					keyword=line[i..].Trim()
+					section="keyword"
+				Case "returns:"
+					returns=line[i..].Trim()+"~n"
+					section="returns"
+				Case "about:"
+					about=line[i..].Trim()+"~n"
+					section="about"
+				Case "param:"
+					If Not params params=New TList
+					params.AddLast line[6..].Trim()
+					section="param"
+				Default
+					Select section
+					Case "about"
+						about:+line+"~n"
+					Case "returns"
+						returns:+" "+line
+					Case "param"
+						params.AddLast String( params.RemoveLast() )+" "+line
+					Default
+						'remaining sections 1 line only...
+						If line Print "Error: Illegal bbdoc section in '"+filePath+"'"
+					End Select
+				End Select
+			
+			EndIf
+		
+		Else If id="rem"
+		
+			bbdoc=""
+			inrem=True
+			
+		Else If id="endtype"
+
+			If typePath
+				docPath=typePath
+				typePath=""
+			EndIf
+			
+		Else If id="import" Or id="include"
+		
+			Local p$=ExtractDir( filePath )+"/"+ParseString( line,i )
+			
+			If ExtractExt( p ).ToLower()="bmx"
+				docBmxFile p,docPath
+			EndIf
+		
+		Else If bbdoc
+		
+			Local kind$,proto$
+			
+			If keyword
+				id=keyword
+				kind="Keyword"
+				If id.StartsWith( "~q" ) And id.EndsWith( "~q" )
+					id=id[1..id.length-1]
+				EndIf
+				proto=id
+			Else If id
+				For Local t$=EachIn AllKinds
+					If id<>t.ToLower() Continue
+					kind=t
+					proto=line
+					id=ParseIdent( line,i )
+					Exit
+				Next
+			EndIf
+			
+			If kind
+
+				Local path$
+
+				Select kind
+				Case "Type"
+					If Not docPath Throw "No doc path"
+					If typePath Throw "Type path already set"
+					typePath=docPath
+					docPath:+"/"+id
+					path=docPath
+				Case "Module"
+					If docPath Throw "Doc path already set"
+					If bbdoc.FindLast( "/" )=-1
+						bbdoc="Other/"+bbdoc
+					EndIf
+					'docPath="/Modules/"+bbdoc
+					Local idLower:String = id.ToLower()
+					docPath = "/" + idLower[..idLower.find(".")] + "/" + idLower
+					path=docPath
+					Local i=bbdoc.FindLast( "/" )
+					bbdoc=bbdoc[i+1..]
+				Default
+					If Not docPath Throw "No doc path"
+					path=docPath+"/"+id
+				End Select
+				
+				Local i=proto.Find( ")=" )
+				If i<>-1 
+					proto=proto[..i+1]
+					If id.StartsWith( "Sort" ) proto:+" )"	'lazy!!!!!
+				EndIf
+				i=proto.Find( "=New" )
+				If i<>-1
+					proto=proto[..i]
+				EndIf
+				
+				Local node:TDocNode=TDocNode.Create( id,path,kind )
+				
+				node.proto=proto
+				node.protoId = BuildProtoId(proto)
+				node.bbdoc=bbdoc
+				node.returns=returns
+				node.about=about
+				node.params=params
+				
+				If kind="Module" node.docDir=docDir
+				
+				Local tmpExampleFilePath$ = CasedFileName(docDir+"/"+id+".bmx")
+				If docDir And FileType( tmpExampleFilePath )=FILETYPE_FILE
+					node.example=StripDir(tmpExampleFilePath)
+				EndIf
+				
+			EndIf
+			
+			bbdoc=""
+
+		EndIf
+	Next
+	
+End Function
+
+Function BuildProtoId:String(proto:String)
+	' function-stripdir-path"
+	Local s:String
+	Local previousIdentChar:Int = False
+	For Local n:Int = EachIn proto.Trim()
+		If IsProtoIdentChar(n) Then
+			s :+ Chr(n)
+			previousIdentChar = True
+		Else
+			If previousIdentChar Then
+				s :+ "-"
+			End If
+			previousIdentChar = False
+		End If
+	Next
+	If s.EndsWith("-") Then
+		s = s[..s.Length-1]
+	End If
+	
+	Return s.ToLower()
+End Function
+

binární
src/makemd/makemd.exe


+ 137 - 0
src/makemd/mdstyle.bmx

@@ -0,0 +1,137 @@
+
+Strict
+
+Import "docstyle.bmx"
+
+Type TRstStyle Extends TDocStyle
+
+	Method EmitHeader()
+
+
+		If doc.kind = "/" Return
+
+		If doc.kind = "Module" Or doc.kind = "Type" Then
+			Emit "---"
+			Emit "id: " + doc.id.ToLower() 
+			Emit "title: " + doc.id
+			Emit "sidebar_label: " + doc.id
+			Emit "---"
+			Emit ""
+		End If
+		
+		Local s:String
+		
+		If doc.kind <> "Module" And doc.kind <> "Type" Then
+			Emit s + doc.id
+		End If
+
+		If doc.kind = "Type" And doc.bbdoc Then
+			Emit doc.bbdoc
+			Emit ""
+		EndIf
+		
+		Emit ""
+
+		If doc.about Then
+			Emit doc.about
+			Emit ""
+		End If
+		
+	End Method
+	
+	Method EmitFooter()
+	
+		If doc.kind = "/" Return
+
+	End Method
+	
+	Method EmitLinks( kind$ )
+		Local list:TList=ChildList( kind )
+		If Not list Return
+		
+		'emit anchor: _Const, _Function etc...
+		
+		If kind="/"
+		
+			Emit "<table class=doc cellspacing=3>"
+			
+			For Local t:TDocNode=EachIn list
+			
+				Emit "<tr><td class=docleft width=1%> #{"+t.id+"}</td></tr>"
+	
+			Next
+	
+			Emit "</table>"
+		
+		Else
+		
+			Emit "## "+kind+"s"
+		
+			Emit "| Type | Description |"
+			Emit "|---|---|"
+			
+			For Local t:TDocNode=EachIn list
+				Emit "| #"+t.id+" | " +t.bbdoc+" |"
+			Next
+	
+			Emit ""
+		EndIf
+	End Method
+	
+	Method EmitDecls( parent:TDocNode, kind$ )
+		Local list:TList=ChildList( kind )
+		If Not list Return
+
+		Emit "## " + kind + "s"
+
+		Emit ""
+		
+		For Local t:TDocNode=EachIn list
+
+			Local s:String
+			
+			Emit "### `" + t.proto + "`"
+			Emit ""
+			
+			If t.bbdoc
+				Emit t.bbdoc
+				Emit ""
+			EndIf
+
+			If t.about
+				For Local line:String = EachIn t.about.Split("~n")
+					Emit line
+				Next
+				Emit ""
+			EndIf
+ 
+			If t.returns
+				Emit "#### Returns"
+
+				Emit t.returns
+				Emit ""
+			EndIf
+			
+			'indent :- 1
+
+			If t.example 
+				Emit "#### Example"
+				Emit "```blitzmax"
+
+				Local p:String = t.example
+
+				Local code$=LoadText( absDocDir+"/"+p).Trim()
+				For Local line:String = EachIn code.Split("~n")
+					Emit line
+				Next
+				Emit "```"
+
+			EndIf
+
+
+			Emit ""
+			
+		Next
+	End Method
+
+End Type

+ 69 - 0
src/makemd/parse.bmx

@@ -0,0 +1,69 @@
+
+Strict
+
+Function IsAlphaChar( char )
+	Return (char>=Asc("A") And char<=Asc("Z")) Or (char>=Asc("a") And char<=Asc("z")) Or (char=Asc("_"))
+End Function
+
+Function IsProtoAlphaChar( char )
+	Return (char>=Asc("A") And char<=Asc("Z")) Or (char>=Asc("a") And char<=Asc("z"))
+End Function
+
+Function IsDecChar( char )
+	Return (char>=Asc("0") And char<=Asc("9"))
+End Function
+
+Function IsIdentChar( char )
+	Return IsAlphaChar( char ) Or IsDecChar( char )
+End Function
+
+Function IsProtoIdentChar( char )
+	Return IsProtoAlphaChar( char ) Or IsDecChar( char )
+End Function
+
+Function IsHexChar( char )
+	Return IsDecChar( char ) Or (char>=Asc("A") And char<=Asc("F")) Or (char>=Asc("a") And char<=Asc("f"))
+End Function
+
+Function IsBinChar( char )
+	Return (char>=Asc("0") And char<=Asc("1"))
+End Function
+
+Function IsPunctChar( char )
+	Select char
+	Case Asc("."),Asc(","),Asc(";"),Asc(":" ),Asc("!"),Asc("?") Return True
+	End Select
+End Function
+
+Function ParseWS$( t$,i Var )
+	Local i0=i
+	While i<t.length And t[i]<=32
+		i:+1
+	Wend
+	Return t[i0..i]
+End Function
+
+Function ParseIdent$( t$,i Var )
+	ParseWS t,i
+	Local i0=i
+	If i<t.length And IsAlphaChar( t[i] )
+		i:+1
+		While i<t.length And (IsIdentChar( t[i] ) Or t[i]=Asc("."))
+			i:+1
+		Wend
+	EndIf
+	Return t[i0..i]
+End Function
+
+Function ParseString$( t$,i Var )
+	ParseWS t,i
+	If i=t.length Or t[i]<>Asc("~q") Return
+	i:+1
+	Local i0=i
+	While i<t.length And t[i]<>Asc("~q" )
+		i:+1
+	Wend
+	Local q$=t[i0..i]
+	If i<t.length i:+1
+	Return q
+End Function