REBOL [
    Title: "eText"
    Date: 3-Sep-2002
    Name: 'eText
    Version: 1.2.1
    File: %etext.r
    Author: "Andrew Martin"
    Needs: [
    %Common%20Parse%20Values.r 
    %ML.r
]
    Purpose: "Processes plain text to HTML."
    eMail: Al.Bri@xtra.co.nz
    Web: http://valley.150m.com
    library: [
        level: 'advanced 
        platform: none 
        type: 'tool 
        domain: [file-handling text-processing] 
        tested-under: none 
        support: none 
        license: none 
        see-also: none
    ]
]

make object! [
	Link_Base: none
	Link_Wiki: false
	Space: charset [#" " #"^-"]
	Separator: charset [#"." #"!" #" " #"," #"?" #";" #":"]
	Empty: [any Space newline]
	Inline!: make object! [
		Text: Block: Before: After: none
		Plain: function [Value [block! string! tag! none!]][String][
			String: copy/part Before After
			if not empty? String [
				append Block String
				]
			if not none? Value [
				repend Block Value
				]
			]
		Pair: function [Mark [char!] HtmlDialect [block!]][NonMark Temporary][
			NonMark: exclude Graphic charset to string! Mark
			compose/deep [
				Temporary: (Mark) copy Text [some (NonMark) any [opt #" " some (NonMark)]]
				(Mark)
				(to-paren reduce ['Plain HtmlDialect])
				(to-paren [After: Temporary])
				Before:
				]
			]
		Link: make object! [
			Word: [Alpha any [AlphaDigit | #"-"] opt {'s} opt #"/"]
			Text: Link: URL: none
			URL_Mail: func [URL [string!]] [
				if 2 <= length? URL [
					URL: first load/next URL
					if email? URL [URL: join "mailto:" URL]
					]
				URL
				]
			ImageAnchor: func [Text [string!] URL [string!]][
				URL: URL_Mail URL
				all [
					file? URL
					URL: join Link_Base URL
					]
				Plain either any [
					found? find/last URL %.jpg
					found? find/last URL %.gif
					found? find/last URL %.png
					][
					['img/src URL]
					][
					['a/href URL Text]
					]
				]
			Rule: [
				[
					After: [
						#"^"" copy Text to #"^"" skip
						#" " [
							#"%" #"^"" copy Link to #"^"" skip (
								insert replace/all Link #" " "%20" #"%"
								)
							| copy Link URI
							]
						#" " copy URL URI
						](
						Link: first load/next Link
						all [
							file? Link
							Link: join Link_Base Link
							]
						Plain [
							'a/href URL_Mail URL reduce [
								'img/src Link Text
								]
							]
						) Before:
					]
				| [
					After: [
						{"} copy Text to {" } {" } [
							#"%" #"^"" copy Link to #"^"" skip (
								insert replace/all Link #" " "%20" #"%"
								)
							| copy Link URI
							]
						] (ImageAnchor Text Link) Before:
					]
				| After: copy Link URI (ImageAnchor copy Link Link) Before:
				| [
					After: [
						{?"} copy Link to {"} skip
						| "?" copy Link Word
						](
						Text: copy Link
						Link: rejoin [
							Link_Base
							to-file replace/all Link #" " "%20"
							either Link_Wiki [""] [
								either #"/" = last Link [%index.html][%.html]
								]
							]
						Plain ['a/href Link Text]
						) Before:
					]
				]
			]
		DoubleQuote: make object! [
			Mark: #"^""
			NonMark: exclude Graphic charset to-string Mark
			Rule: [
				After: Mark copy Text [NonMark any [NonMark | #" "]] Mark (
					Plain [rejoin ["“" Text "”"]]
					) Before:
				]
			]
		SingleQuote: make object! [
			Mark: #"'"
			Div: none
			NonMark: exclude Graphic charset to-string Mark
			Rule: [
				After: #" " Mark copy Text [NonMark some [NonMark | #" "]]
					Mark copy Div Separator(
					Plain [rejoin [#" " "‘" Text "’" Div]]
					) Before:
				]
			]
		Superscript: make object! [
			Rule: [
				After: #"^^" copy Text [some Alpha | Digits] (
					Plain reduce [  Text  ]
					) Before:
				]
			]
		Single: func [Mark [string! char!] Replacement [string!]][
			compose [After: (Mark) (to-paren compose [Plain (Replacement)]) Before:]
			]
		Rules: compose [
			(Link/Rule)
			| (DoubleQuote/Rule)
			| (SingleQuote/Rule)
			| (Pair #"_" ['u Text])
			| (Pair #"~" ['i Text])
			;| (Pair #"+" ['ins Text])
			;| (Pair #"-" ['del Text])	; Need a better choice for 'Del, not hyphen.
			| (Pair #"*" ['b Text])
			| (Single newline "
") | (Single {---} "—") | (Single {--} "–") | (Single {&} "&") | (Single {<} "<") | (Single {>} ">") | (Single {(c)} "©") | (Single {(C)} "©") | (Single {(r)} "®") | (Single {(R)} "®") | (Single {(tm)} "™") | (Single {(TM)} "™") | (Single {-tm} "™") | (Single {-TM} "™") | (Single {A^^`} {À}) | (Single {a^^`} {à}) | (Single {A^^'} {Á}) | (Single {a^^'} {Á}) | (Single {A^^~} {Ã}) | (Single {a^^~} {ã}) | (Single {A^^"} {Ä}) | (Single {a^^"} {ä}) | (Single {A^^*} {Å}) | (Single {a^^*} {å}) | (Single {A^^E} {Æ}) | (Single {a^^e} {æ}) | (Single {,C} {Ç}) | (Single {,c} {&ccdel;}) | (Single {E^^`} {È}) | (Single {e^^`} {è}) | (Single {E^^'} {É}) | (Single {e^^'} {é}) | (Single {E^^"} {Ë}) | (Single {e^^"} {ë}) | (Single {I^^`} {Ì}) | (Single {i^^`} {ì}) | (Single {I^^'} {Í}) | (Single {i^^'} {í}) | (Single {I^^"} {Ï}) | (Single {i^^"} {ï}) | (Single {D^^-} {Ð}) | (Single {d^^-} {ð}) | (Single {N^^~} {Ñ}) | (Single {n^^~} {ñ}) | (Single {O^^`} {Ò}) | (Single {o^^`} {ò}) | (Single {O^^'} {Ó}) | (Single {o^^'} {ó}) | (Single {O^^~} {Õ}) | (Single {o^^~} {õ}) | (Single {O^^"} {Ö}) | (Single {o^^"} {ö}) | (Single {O^^/} {Ø}) | (Single {o^^/} {ø}) | (Single {O^^E} {Œ}) | (Single {o^^e} {œ}) | (Single {U^^`} {Ù}) | (Single {u^^`} {ù}) | (Single {U^^'} {Ú}) | (Single {u^^'} {ú}) | (Single {U^^"} {Ü}) | (Single {u^^"} {ü}) | (Single {Y^^'} {Ý}) | (Single {y^^'} {ý}) | (Single {Y^^"} {Ÿ}) | (Single {y^^"} {ÿ}) | (Single {S^^z} {ß}) | (Single {P|} {Þ}) | (Single {p|} {þ}) | (Single {~!} {¡}) | (Single {~?} {¿}) | (Single {c^^/} {¢}) | (Single {L^^-} {£}) | (Single {Y^^-} {&Yen;}) | (Single {o^^$} {¤}) | (Single {||} {¦}) | (Single {<<} {«}) | (Single {>>} {»}) | (Single {-,} {¬}) | (Single {^^-} {¯}) | (Single {^^o} {°}) | (Single {^^o-} {º}) ; { 1/4 } { ¼ } { 1/2 } { ½ } { 3/4 } { ¾ } | (Single {''} {´}) | (Single {^^/u} {µ}) | (Single {P^^!} {¶}) | (Single {sO} {§}) | (Single {^^.} {·}) | (Single {,,} {¸}) | (Single {...} {…}) | (Single { +- } { ± }) | (Single { * } { × }) | (Single {-:} {÷}) ;| (Single { / } { ÷ }) ; Slash is often used as a divider or alternative. | (Single {A^^} {Â}) | (Single {a^^} {â}) | (Single {E^^} {Ê}) | (Single {e^^} {ê}) | (Single {I^^} {Î}) | (Single {i^^} {î}) | (Single {O^^} {Ô}) | (Single {o^^} {ô}) | (Single {U^^} {Û}) | (Single {u^^} {û}) ;{ pi } {π} | (Single {sqrt} {√}) ; {Ö} | (Superscript/Rule) | skip ] Dialect: func [String [string!]][ Block: make block! 10 Before: String After: none parse/case/all String [some Rules (Plain None) end] either empty? Block [ String ][ Block ] ] Literal-Rules: compose [ (Single {&} "&") | (Single {<} "<") | (Single {>} ">") | skip ] Literal: func [String [string!]][ Block: make block! 10 Before: String After: none parse/case/all String [some Literal-Rules (Plain None) end] either empty? Block [ String ][ Block ] ] ] Inline: get in Inline! 'Dialect Literal: get in Inline! 'Literal Line: Heading: Block: Previous: none Text-Line: [Graphic any Printable] Text: [copy Line Text-Line empty] H: [ opt Empty Text [ some "*" (Heading: 'h1) | some "=" (Heading: 'h2) | some "-" (Heading: 'h3) | some "~" (Heading: 'h4) | some "_" (Heading: 'h5) | some "." (Heading: 'h6) ] empty (repend Block [Heading Inline Line]) ] IP: [Text (repend Block ['p/class "Initial" Inline Line])] RP: [2 Empty Text (repend Block [
'p/class "Initial" Inline Line])] P: [[Empty | tab | #" "] Text (repend Block ['p Inline Line])] Align!: make object! [ Type: 'left Rule: [#" " (Type: 'center) | tab (Type: 'right) | none (Type: 'left)] ] Align: Align!/Rule Center: make object! [ Lines: make block! 10 Rule: [ some [ #" " copy Line [Graphic any Printable] empty ( if not empty? Lines [ append Lines
] append Lines Inline Line ) ]( repend Block ['div/align "center" Lines] Lines: make block! 10 ) ] ] Table: make object! [ Type: 'th Mark: #"|" NonBar: exclude Printable charset to-string Mark Cells: make block! 10 BarCell: [Align copy Line any NonBar any [#" " | tab]] TabCell: [Align copy Line any Printable] Append-Cell: does [ repend Cells [ make path! reduce [Type 'align] Align!/Type either none? Line [""][Inline trim Line] ] ] Row: [ [ opt [some [Mark some #"-"] opt Mark empty] some [Mark BarCell (Append-Cell)] opt Mark empty ] | TabCell (Append-Cell) some [tab TabCell (Append-Cell)] empty ] Rows: make block! 10 Rule: [ opt Empty ( Type: 'th Rows: make block! 10 Cells: make block! 10 ) some [ Row ( repend Rows ['tr Cells] Type: 'td Cells: make block! 10 ) ] ( repend Block ['table Rows] ) ] ] Quote: make object! [ Quotes: make string! 100 Rule: [ opt Empty some [ 2 [tab | #" "] copy Line some [Printable | tab] empty ( append Quotes rejoin [trim/tail Line newline] ) ] ( repend Block ['blockquote reduce ['pre Literal detab Quotes]] clear Quotes ) ] ] BlockQuote: make object! [ Center: no NonQuote: exclude Graphic charset {"} Lines: make block! 10 Common: function [L [string! block!]] [bq] [ bq: [ 'i reduce [ 'blockquote either string? L [inline L] [L] ] ] repend Block either Center [ [ 'div/align "center" reduce bq ] ] [ bq ] Center: no ] Rule: [ [ opt [#" " (Center: true)] (Lines: make block! 10) #"^"" copy Line [some [NonQuote | { "} | {" } | { }]] #"^"" empty ( Common Line ) ] | [ opt [#" " (Center: true)] (Lines: make block! 10) #"^"" copy Line [some [NonQuote | { "} | {" } | { }]] empty ( repend Lines [Line
] ) any [ opt #" " copy Line [some [NonQuote | { "} | {" } | { }]] empty ( repend Lines [Line
] ) ] opt #" " copy Line [some [NonQuote | { "} | {" } | { }]] #"^"" empty ( append Lines Line Common Lines ) ] ] ] List: make object! [ ULI: [#"*" [tab | #" "] Text] OLI: [#"0" [tab | #" "] Text] Term: Definition: none DT: [copy Term Text-Line empty] DD: [[tab | #" "] copy Definition Text-Line empty] Br: [opt Empty] Item: func [Block [block!] /DL][ repend Block either DL [ ['dt Inline Term 'dd Inline Definition] ] [ ['li Inline Line] ] ] Nest: func [Outer [block!] 'Word [word!] Items [block!]][ repend Outer [Word Items] make block! length? Items ] LIs: make block! 1 UL: [some [Br ULI (Item LIs) | UL1 | OL1 | DL1] (LIs: Nest Block ul LIs)] OL: [some [Br OLI (Item LIs) | OL1 | UL1 | DL1] (LIs: Nest Block ol LIs)] DL: [some [Br DT DD (Item/DL LIs) | DL1 | UL1 | OL1] (LIs: Nest Block dl LIs)] Tab1: [tab | #" "] LI1s: make block! 1 UL1: [some [Br Tab1 ULI (Item LI1s) | UL2 | OL2 | DL2] (LI1s: Nest LIs ul LI1s)] OL1: [some [Br Tab1 OLI (Item LI1s) | OL2 | UL2 | DL2] (LI1s: Nest LIs ol LI1s)] DL1: [some [Br Tab1 DT Tab1 DD (Item/DL LI1s) | DL2 | UL2 | OL2] (LI1s: Nest LIs dl LI1s)] Tab2: [2 Tab1] LI2s: make block! 1 UL2: [some [Br Tab2 ULI (Item LI2s) | UL3 | OL3 | DL3] (LI2s: Nest LI1s ul LI2s)] OL2: [some [Br Tab2 OLI (Item LI2s) | OL3 | UL3 | DL3] (LI2s: Nest LI1s ol LI2s)] DL2: [some [Br Tab2 DT Tab2 DD (Item/DL LI2s) | DL3 | UL3 | OL3] (LI2s: Nest LI1s dl LI2s)] Tab3: [3 Tab1] LI3s: make block! 1 UL3: [some [Br Tab3 ULI (Item LI3s) | UL4 | OL4 | DL4] (LI3s: Nest LI2s ul LI3s)] OL3: [some [Br Tab3 OLI (Item LI3s) | OL4 | UL4 | DL4] (LI3s: Nest LI2s ol LI3s)] DL3: [some [Br Tab3 DT Tab3 DD (Item/DL LI3s) | DL4 | UL4 | OL4] (LI3s: Nest LI2s dl LI3s)] Tab4: [4 Tab1] LI4s: make block! 1 UL4: [some [Br Tab4 ULI (Item LI4s) | UL5 | OL5 | DL5] (LI4s: Nest LI3s ul LI4s)] OL4: [some [Br Tab4 OLI (Item LI4s) | OL5 | UL5 | DL5] (LI4s: Nest LI3s ol LI4s)] DL4: [some [Br Tab4 DT Tab4 DD (Item/DL LI4s) | DL5 | UL5 | OL5] (LI4s: Nest LI3s dl LI4s)] Tab5: [5 Tab1] LI5s: make block! 1 UL5: [some [Br Tab5 ULI (Item LI5s) | UL6 | OL6 | DL6] (LI5s: Nest LI4s ul LI5s)] OL5: [some [Br Tab5 OLI (Item LI5s) | OL6 | UL6 | DL6] (LI5s: Nest LI4s ol LI5s)] DL5: [some [Br Tab5 DT Tab5 DD (Item/DL LI5s) | DL6 | UL6 | OL6] (LI5s: Nest LI4s dl LI5s)] Tab6: [6 Tab1] LI6s: make block! 1 UL6: [some [Br Tab6 ULI (Item LI6s) | UL7 | OL7 | DL7] (LI6s: Nest LI5s ul LI6s)] OL6: [some [Br Tab6 OLI (Item LI6s) | OL7 | UL7 | DL7] (LI6s: Nest LI5s ol LI6s)] DL6: [some [Br Tab6 DT Tab6 DD (Item/DL LI6s) | DL7 | UL7 | OL7] (LI6s: Nest LI5s dl LI6s)] Tab7: [7 Tab1] LI7s: make block! 1 UL7: [some [Br Tab7 ULI (Item LI7s)] (LI7s: Nest LI6s ul LI7s)] OL7: [some [Br Tab7 OLI (Item LI7s)] (LI7s: Nest LI6s ol LI7s)] DL7: [some [Br Tab7 DT Tab7 DD (Item/DL LI7s)] (LI7s: Nest LI6s dl LI7s)] Rule: [opt Empty [UL | OL | DL]] ] VerticalSpace: [some [empty (append Block 'br)]] Statements: make object! [ Lines: make block! 1 Rule: [ some [Text (append Lines append Inline Line
)] ( remove back tail Lines repend Block ['p/class "Initial" Lines] Lines: make block! 10 ) ] ] BulletDivider: [ Empty " *" empty Empty ( append Block [
div/align "center" "•"
] ) ] LineDivider: [ Empty 3 #"-" any #"-" empty Empty (append Block
) ] Rules: compose/deep [ any [ BulletDivider | LineDivider | H opt [(Quote/Rule) | (List/Rule)| (Table/Rule) | (BlockQuote/Rule) | IP] | (Quote/Rule) | (List/Rule) | (Table/Rule) | (BlockQuote/Rule) | (Center/Rule) | RP | P | VerticalSpace | (Statements/Rule) ] end ] set 'eText func [ "Processes plain text into HTML." eText [string!] "The plain text." /Wiki "Format for a Wiki." /Base Base_URL [url! file! string!] "Base URL for references." ][ Link_Wiki: Wiki Link_Base: either Base [Base_URL] [""] Block: make block! 1000 if not empty? eText [ if newline <> last eText [append eText newline] parse/all eText Rules ] Block ] ]