text: Rebol [
  	Title: "CSSR"
  	Purpose: "A Style Sheet Dialect that generates CSS"
  	Version: 0.1.7
  	Date: 17-Jun-2013
  	Author: "Christopher Ross-Gill"
  	Name: 'cssr
  	Type: 'module
  	Exports: [to-css]
  ]
  
  to-css: use [ruleset parser ??][
  
  ruleset: context [
  	; Values
  	values: copy []
  	unset: func [key [word!]][remove-each [k value] values [k = key]]
  	set: func [key [word!] value [any-type!]][
  		unset key repend values [key value]
  	]
  
  	; Values that are zero or more
  	colors: copy []
  	lengths: copy []
  	; images: copy []
  	transitions: copy []
  	transformations: copy []
  	; spacing: copy []
  
  	enspace: func [value][join " " value]
  
  	form-color: func [value [tuple! word!]][
  		enspace either value/4 [
  			["rgba(" value/1 "," value/2 "," value/3 "," either integer? value: value/4 / 255 [value][round/to value 0.01] ")"]
  		][
  			["rgb(" value/1 "," value/2 "," value/3")"]
  		]
  	]
  
  	form-number: func [value [number!] unit [word! string! none!]][
  		enspace case [
  			value = 0 ["0"]
  			unit [join value unit]
  			value [form value]
  		]
  	]
  
  	form-value: func [values /local value choices][
  		any [
  			switch value: take values [
  				em pt px deg vw vh [form-number take values value]
  				pct [form-number take values "%"]
  				* [form-number take values none]
  				| [","]
  				radial [enspace ["radial-gradient(" remove form-values values ")"]]
  				linear [enspace ["linear-gradient(" remove form-values values ")"]]
  			]
  			switch type?/word value [
  				integer! decimal! [form-number value 'px]
  				pair! [rejoin [form-number value/x 'px form-number value/y 'px]]
  				time! [form-number value/second 's]
  				tuple! [form-color value]
  				string! [enspace mold value]
  				url! file! [enspace [{url('} value {')}]]
  				path! [enspace [{url("data:} form value {;base64,} enbase/base take values 64 {")}]]
  			]
  			enspace value
  		]
  	]
  
  	form-transform: func [transform [block!] /local name direction][
  		; [
  		; 	  'translate direction length
  		; 	| 'rotate angle opt ['origin percent percent]
  		; 	| 'scale [direction number | 1 2 number]
  		; ]
  
  		switch/default take transform [
  			translate [
  				enspace [
  					"translate" uppercase form take transform
  					"(" next form-value transform ")"
  				]
  			]
  			rotate [
  				enspace ["rotate(" next form-value transform ")"]
  			]
  			scale [
  				enspace [
  					"scale" either word? transform/1 [uppercase form take transform][""]
  					"(" next form-number take transform none either tail? transform [""][
  						"," form-number take transform none
  					] ")"
  				]
  			]
  		][keep mold head insert transform name]
  	]
  
  	form-values: func [values [block!]][
  		rejoin collect [
  			while [not tail? values][keep form-value values]
  		]
  	]
  
  	form-property: func [property [word!] values [string! block!] /vendors /inline prefix][
  		if block? values [values: form-values values]
  		rejoin collect [
  			if any [vendors found? find [transition box-sizing transform-style transition-delay] property][
  				foreach prefix [-webkit- -moz- -ms- -o-][
  					keep form-property to word! join prefix form property values
  				]
  			]
  			if prefix [insert next values prefix]
  			keep ["^/^-" property ":" values ";"]
  		]
  	]
  
  	render: has [value][
  		; sort/skip values 2
  		while [value: take lengths][
  			value: compose [(value)]
  			case [
  				not find values 'width [set 'width value]
  				not find values 'height [set 'height value]
  			]
  		]
  		while [value: take colors][
  			value: compose [(value)]
  			case [
  				not find values 'color [set 'color value]
  				not find values 'background-color [set 'background-color value]
  			]
  		]
  		rejoin collect [
  			keep "{"
  			foreach [property values] values [
  				case [
  					find [opacity] property [
  						if tail? next values [insert values '*]
  					]
  					all [
  						property = 'background-image
  						find [radial linear] values/1
  					][
  						foreach prefix [-webkit- -moz- -ms- -o-][
  							keep form-property/inline property copy values prefix
  						]
  					]
  					
  				]
  				switch/default property [][
  					keep form-property property values
  				]
  			]
  			foreach transform transformations [
  				transform: form-transform transform
  				keep form-property/vendors 'transform transform
  			]
  			unless empty? transitions [
  				keep form-property/vendors 'transition rejoin next collect [
  					foreach transition transitions [
  						keep ","
  						keep form-values transition
  					]
  				]
  			]
  			keep "^/}"
  		]
  	]
  
  	new: does [
  		make self [
  			values: copy []
  			colors: copy []
  			lengths: copy []
  			; dimensions: copy []
  			; images: copy []
  			transitions: copy []
  			transformations: copy []
  			spacing: copy []
  		]
  	]
  ]
  
  parser: context [
  	google-fonts-base-url: http://fonts.googleapis.com/css?family=
  
  	; Storage
  	reset?: false
  	rules: []
  	google-fonts: []
  
  	; Basic Types
  	zero: use [zero][
  		[set zero integer! (zero: either zero? zero [[]][[end skip]]) zero]
  	]
  	em: ['em number! | zero]
  	pt: ['pt number!]
  	px: [opt 'px number!]
  	deg: ['deg number! | zero]
  	scalar: ['* number! | zero]
  	percent: ['pct number! | zero]
  	vh: ['vh number! | zero]
  	vw: ['vw number! | zero]
  	color: [tuple! | named-color]
  	time: [time!]
  	pair: [pair!]
  	binary: [end skip] ; [path! binary!] ; omitted until considered safe
  	image: [binary | file! | url!]
  
  	; Optionals
  	named-color: [
  		'aqua | 'black | 'blue | 'fuchsia | 'gray | 'green |
  		'lime | 'maroon | 'navy | 'olive | 'orange | 'purple |
  		'red | 'silver | 'teal | 'white | 'yellow
  	]
  	text-style: ['bold | 'italic | 'underline]
  	border-style: ['solid | 'dotted | 'dashed]
  	transition-attribute: [
  		  'width | 'height | 'top | 'bottom | 'right | 'left | 'z-index
  		| 'background | 'color | 'border | 'opacity | 'margin
  		| 'transform | 'font | 'indent | 'spacing
  	]
  	list-styles: [
  		  'disc | 'circle | 'square | 'decimal | 'decimal-leading-zero
  		| 'lower-roman | 'upper-roman | 'lower-greek | 'lower-latin
  		| 'upper-latin | 'armenian | 'georgian | 'lower-alpha | 'upper-alpha
  	]
  	direction: ['x | 'y | 'z]
  	position-x: ['right | 'left | 'center]
  	position-y: ['top | 'bottom | 'middle]
  	position: [position-y | position-x]
  	positions: [position-y position-x | position-y | position-x]
  	repeats: ['repeat-x | 'repeat-y | 'repeat ['x | 'y] | 'no-repeat | 'no 'repeat]
  	font-name: [string! | 'sans-serif | 'serif | 'monospace]
  	length: [em | pt | px | percent | vh | vw]
  	angle: [deg]
  	number: [scalar | number!]
  	box-model: ['block | 'inline 'block | 'inline-block]
  
  	; Capture/Use System
  	; parse block [mark ... capture (:captured)]
  	mark: capture: captured: none
  	use [start extent][
  		mark: [start:]
  		capture: [extent: (new-line/all captured: copy/part start extent false)]
  	]
  	emit: func [name [word!] value [any-type!]][
  		value: compose [(value)]
  		; change all the non-standard words
  		foreach [from to][
  			[no repeat] 'no-repeat
  			[no bold] 'normal
  			[no italic] 'normal
  			[no underline] 'none
  			[inline block] 'inline-block
  			[line height] 'line-height
  		][
  			replace value from to
  		]
  		current/set name value
  	]
  	emits: func [name [word!]][
  		emit name captured
  	]
  
  	; The All-Powerful Selector Rule
  	; Must be a way to simplify this.
  	selector: use [
  		dot-word primary qualifier
  		form-element form-selectors
  		out selectors selector
  	][
  		dot-word: use [word continue][
  			; Matches only words that begin .something
  			[
  				set word word!
  				(continue: either #"." = take form word [[]][[end skip]])
  				continue
  			]
  		]
  
  		primary: [tag! | issue! | dot-word]
  		qualifier: [primary | get-word!]
  
  		form-element: func [element [tag! issue! word! get-word!]][
  			either tag? element [to string! element][mold element]
  		]
  
  		form-selectors: func [selectors [block!]][
  			selectors: collect [
  				parse selectors [
  					some [mark some qualifier capture (keep/only captured)
  					| word! capture (keep captured)]
  				]
  			]
  
  			selectors: collect [
  				while [find selectors 'and][
  					keep/only copy/part selectors selectors: find selectors 'and
  					selectors: next selectors
  				] keep/only copy selectors
  			]
  
  			selectors: map-each selector selectors [
  				collect [
  					foreach selector reverse collect [
  						while [find selector 'in][
  							keep/only copy/part selector selector: find selector 'in
  							keep 'has
  							selector: next selector
  						] keep/only copy selector
  					][keep selector]
  				]
  			]
  
  			selectors: collect [
  				foreach selector selectors [
  					parse selector [
  						set selector block! (selector: map-each element selector [form-element element])
  						any [
  							'with mark block! capture (
  								selector: collect [
  									foreach selector selector [
  										foreach element captured/1 [
  											keep join selector form-element element
  										]
  									]
  								]
  							) |
  							'has mark block! capture (
  								selector: collect [
  									foreach selector selector [
  										foreach element captured/1 [
  											keep rejoin [selector " " form-element element]
  										]
  									]
  								]
  							)
  						]
  					]
  					keep/only selector
  				]
  			]
  
  			rejoin remove collect [
  				foreach selector selectors [
  					foreach rule selector [
  						keep "," keep "^/"
  						keep rule
  					]
  				]
  			]
  		]
  
  		selector: [
  			some primary any [
  				  'with some qualifier
  				| 'in some primary
  				| 'and selector
  			]
  		]
  
  		[
  			mark
  			some primary any [
  				  'with some qualifier
  				| 'in some primary
  				| 'and selector
  			] capture
  			(repend rules [form-selectors captured current: ruleset/new])
  		]
  	]
  
  	; Each of the Properties fully BNFed
  	property: [
  		  mark box-model capture (emits 'display)
  		| mark 'border-box capture (emits 'box-sizing)
  		| 'min some [
  			  'width mark length capture (emits 'min-width)
  			| 'height mark length capture (emits 'min-height)
  		]
  		| 'max some [
  			  'width mark length capture (emits 'max-width)
  			| 'height mark length capture (emits 'max-height)
  		]
  		| mark ['min-width | 'min-height | 'max-width | 'max-height] length capture (emits take captured)
  		| 'height mark length capture (emits 'height)
  		| 'margin [
  			mark [
  				  1 2 [length opt [length | 'auto]]
  				| pair opt [length | pair]
  			] capture (emits 'margin)
  			|
  		] any [
  			  'top mark length capture (emits 'margin-top)
  			| 'bottom mark length capture (emits 'margin-bottom)
  			| 'right mark [length | 'auto] capture (emits 'margin-right)
  			| 'left mark [length | 'auto] capture (emits 'margin-left)
  		]
  		| 'padding [
  			mark [
  				  1 4 length
  				| pair opt [length | pair]
  			] capture (emits 'padding)
  			|
  		] any [
  			  'top mark length capture (emits 'padding-top)
  			| 'bottom mark length capture (emits 'padding-bottom)
  			| 'right mark [length | 'auto] capture (emits 'padding-right)
  			| 'left mark [length | 'auto] capture (emits 'padding-left)
  		]
  		| 'border any [
  			  mark 1 4 border-style capture (emits 'border-style)
  			| mark 1 4 color capture (emits 'border-color)
  			| 'radius [
  				some [
  					  'top mark 1 2 length capture (
  						emits 'border-top-left-radius
  						emits 'border-top-right-radius
  					)
  					| 'bottom mark 1 2 length capture (
  						emits 'border-bottom-left-radius
  						emits 'border-bottom-right-radius
  					)
  					| 'right mark 1 2 length capture (
  						emits 'border-top-right-radius
  						emits 'border-bottom-right-radius
  					)
  					| 'left mark 1 2 length capture (
  						emits 'border-top-left-radius
  						emits 'border-bottom-left-radius
  					)
  					| 'top 'right mark 1 2 length capture (emits 'border-top-right-radius)
  					| 'top 'left mark 1 2 length capture (emits 'border-top-left-radius)
  					| 'bottom 'right mark 1 2 length capture (emits 'border-bottom-right-radius)
  					| 'bottom 'left mark 1 2 length capture (emits 'border-bottom-left-radius)
  				]
  				| mark 1 2 length capture (emits 'border-radius)
  			]
  			| mark 1 4 length capture (emits 'border-width)
  		]
  		| ['radius | 'rounded] mark length capture (emits 'border-radius)
  		| 'rounded (emit 'border-radius [em 0.6])
  		| 'font any [
  			  mark length capture (emits 'font-size)
  			| mark some font-name capture (
  				captured
  				remove head forskip captured 2 [insert captured '|]
  				emits 'font-family
  			)
  			| mark color capture (emits 'color)
  			| 'line 'height mark number capture (emits 'line-height)
  			| 'spacing mark number capture (emits 'letter-spacing)
  			| 'shadow mark pair length color capture (emits 'text-shadow)
  			| mark opt 'no 'bold capture (emits 'font-weight)
  			| mark opt 'no 'italic capture (emits 'font-style)
  			| mark opt 'no 'underline capture (emits 'text-decoration)
  			| ['line-through | 'strike 'through] (emit 'text-decoration 'line-through)
  		]
  		| 'text 'indent mark length capture (emits 'text-indent)
  		| 'line 'height mark [length | scalar] capture (emits 'line-height)
  		| 'spacing mark number capture (emits 'letter-spacing)
  		| mark opt 'no 'bold capture (emits 'font-weight)
  		| mark opt 'no 'italic capture (emits 'font-style)
  		| mark opt 'no 'underline capture (emits 'text-decoration)
  		| ['line-through | 'strike 'through] (emit 'text-decoration 'line-through)
  		| 'shadow mark pair length color capture (emits 'box-shadow)
  		| 'color mark [color | 'inherit] capture (emits 'color)
  		| mark ['relative | 'absolute | 'fixed] capture (emits 'position) any [
  			  'top mark length capture (emits 'top)
  			| 'bottom mark length capture (emits 'bottom)
  			| 'right mark length capture (emits 'right)
  			| 'left mark length capture (emits 'left)
  		]
  		| 'opacity mark number capture (emits 'opacity)
  		| mark 'nowrap capture (emits 'white-space)
  		| mark 'center capture (emits 'text-align)
  		| 'transition any [
  			mark transition-attribute time opt time capture (
  				append/only current/transitions captured
  			)
  		]
  		| [
  			  'delay mark time capture (emits 'transition-delay)
  			| mark time opt time transition-attribute capture (
  				append/only current/transitions head reverse next reverse captured
  			)
  			| mark time capture (emits 'transition)
  		]
  		| some [
  			mark [
  				  'translate direction length
  				| 'rotate angle opt ['origin percent percent]
  				| 'scale [['x | 'y] number | 1 2 number]
  			] capture (append/only current/transformations captured)
  		]
  		| mark 'preserve-3d capture (emits 'transform-style)
  		| 'hide (emit 'display none)
  		| 'float mark position-x capture (emits 'float)
  		| 'opaque (emit 'opacity 1)
  		| mark 'pointer capture (emits 'cursor)
  		| ['canvas | 'background] any [
  			  mark color capture (emits 'background-color)
  			| mark [file! | url!] (emits 'background-image)
  			| mark positions capture (emits 'background-position)
  			| mark repeats capture (emits 'background-repeat)
  			| mark ['contain | 'cover] capture (emits 'background-size)
  			| mark pair capture (
  				captured: first captured
  				emit 'background-position reduce [
  					'pct to integer! captured/x
  					'pct to integer! captured/y
  				]
  			)
  		]
  
  		| mark [
  			'radial color color capture (
  				insert at captured 3 '|
  			)
  			| 'linear angle color color capture (
  				insert at tail captured -2 '|
  				insert at tail captured -1 '|
  			)
  			| 'linear opt 'to positions color color capture (
  				unless 'to = captured/2 [insert next captured 'to]
  				insert at tail captured -2 '|
  				insert at tail captured -1 '|
  			)
  		] (emits 'background-image)
  
  		; | mark binary capture (emits 'background-image) any [
  		; 	  mark positions capture (emits 'background-position)
  		; 	| mark repeats capture (emits 'background-repeat)
  		; ]
  		| mark image capture (emits 'background-image) any [
  			  mark positions capture (emits 'background-position)
  			| mark pair capture (
  				captured: first captured
  				emit 'background-position reduce [
  					'pct to integer! captured/x
  					'pct to integer! captured/y
  				]
  			)
  			| mark repeats capture (emits 'background-repeat)
  			| mark ['contain | 'cover] capture (emits 'background-size)
  		]
  		| 'no ['list opt 'style | 'bullet] (emit 'list-style-type 'none)
  		| opt ['list opt 'style | 'bullet] mark list-styles capture (emits 'list-style-type)
  		| mark ['inside | 'outside] capture (emits 'list-style-position)
  
  		; Any Singleton Values
  		| mark [
  			  length capture (append/only current/lengths captured)
  			| some color capture (append current/colors captured)
  			| time capture (emits 'transition)
  			| pair capture (
  				emit 'width captured/1/x
  				emit 'height captured/1/y
  			)
  		]
  	]
  
  	; Control
  	current: value: none
  	errors: copy []
  
  	; Format Description
  	dialect: [()
  		opt ['css/reset (reset?: true)]
  		opt [
  			'google 'fonts [
  				some [
  					copy value [string! any issue!]
  					(append/only google-fonts value)
  					|
  					set value url! (
  						all [
  							value: find/match value google-fonts-base-url
  							append google-fonts value
  						]
  					)
  				]
  			]
  		]
  
  		[selector | (repend rules [["body"] current: ruleset/new])]
  
  		any [
  			selector | property
  
  			| set value skip (append errors rejoin ["MISPLACED TOKEN: " mold value])
  		]
  	]
  
  	; CSS Reset Stylesheet
  	reset: to string! decompress 64#{
  	eJyNU7Fu2zAQncOvIAwUaQ0pkt0mg4x27pBu3YoMpHiSWFOkTFIOnDT/3kfJNtyi
  	RQKI4pF8vHv37lgseRfjUBVFTwfyjyRvatcX5HVdROdMKOoQCk+BYsEZ53y/vin5
  	L74uV6tytb5LW/e6Jhuo4tZZ4u+HURpdc+V6oe0HtiwY62JvMi6dOmRc6X3GwyBs
  	xsUwGIoZd/In1Zh140VPGetWGe/WGB8xPmHcYtxlfIAP4+rtbnSRsPTACriR0uNf
  	e2cPPQylwDcAq9uM1zpBa6eAVQQWqkFkAk73ONYWwK1UGd+BFT7RDxkLvTCAhuj1
  	lqbZWYDDKNMPNCLY7oXPGDZGeEEEspGwoVIInCq4dLBHDKMz1jiPmEbIxEGOMTrQ
  	KJaNJqNCEsFQS1ZlkCsKaRJnMUSdUHEWLjbOARc7EvAdfTIxFDTwUdfpighaTTft
  	XiAZRVFoE1K6knCHNbodIRrHfPaevII4T26nufUupch6skjNCpTLjXEYEduPEkQC
  	ijVdDWPfC3/IWNQoG4e9BYdRaQd1wMTxZ3aF3VbbipcbdjWgNtq280I6j4Cz3Tgb
  	86Cf0ESrsnx33KlQng6tGLHeU0pSmFwY3cKdFIGMtrRhL6xY8q/fv93forfCYMQh
  	984Qn5oW6XnUAYG49O4xkA8cEv+t2FmpP5Q5qsX+J9GFQkdNUsJHEtXcq4lfql86
  	SXzzjnTbIbVVOpk7ZD4LUCAezPEdTfcumn2XQJMd/gWoJCFTunwglWimjtydz3bz
  	VvJUQ15KCl9fby5WJ8dTCybcXKS8dsaIIb3xk3UuYI63XJ+K+sLY3NtVlffuKW9c
  	PYZcW5uIaIsm+hEPA31eTMVZPLwGm529jsPL7PUb/DXa0OKBf+FvC8Kfj01abk6t
  	W26ODV1ukOxvKxG8Kj8FAAA=
  	}
  
  	; Output
  	render: does [
  		rejoin collect [
  			keep {/* CSSR Output */^/}
  			if all [
  				block? google-fonts
  				not empty? google-fonts
  			][
  				keep "^/@import url ('"
  				keep mold join google-fonts-base-url collect [
  					repeat font length? google-fonts [
  						unless font = 1 [keep "|"]
  						case [
  							url? google-fonts/:font [
  								keep google-fonts/:font
  							]
  							block? google-fonts/:font [
  								keep replace/all mold to url! take google-fonts/:font "%20" "+"
  								repeat variant length? google-fonts/:font [
  									keep back change to url! mold google-fonts/:font/:variant either variant = 1 [":"][","]
  								]
  							]
  						]
  					]
  				]
  				keep "');^/"
  			]
  			if reset? [
  				keep "^//** CSS Reset Begin */^/^/"
  				keep reset
  				keep "/* CSS Reset End **/^/"
  			]
  			keep "^//** CSSR Output Begin */^/^/"
  			foreach [selector rule] rules [
  				keep selector
  				keep " "
  				keep rule/render
  				keep "^/"
  			]
  			keep "^/^//* CSSR Output End **/^/"
  		]
  	]
  
  	; Is Modular
  	new: does [
  		make parser [
  			reset?: false
  			google-fonts: copy []
  			rules: copy []
  			errors: copy []
  			current: ruleset/new
  			value: none
  		]
  	]
  ]
  
  ??: use [mark][[mark: (probe new-line/all copy/part mark 8 false)]]
  
  to-css: func [dialect [file! url! string! block!] /local out][
  	case/all [
  		file? dialect [dialect: load dialect]
  		url? dialect [dialect: load dialect]
  		string? dialect [dialect: load dialect]
  		not block? dialect [make error! "No Dialect!"]
  	]
  
  	out: parser/new
  	if parse dialect out/dialect [
  		out/render
  	]
  ]
  
  ]
  