code: 9ferno

ref: b548687a8ed1d0a159c9d3f3f921d93bbb56908e
dir: /appl/ebook/cssparser.b/

View raw version
implement CSSparser;

include "sys.m";
	sys: Sys;
include "string.m";
	str: String;
include "css.m";
	css: CSS;
	Stylesheet, Statement, Select, Value: import css;
include "cssparser.m";

init()
{
	sys = load Sys Sys->PATH;
	str = load String String->PATH;
	css = load CSS CSS->PATH;
	if (css == nil) {
		sys->fprint(sys->fildes(2), "cssparser: cannot load %s: %r\n", CSS->PATH);
		raise "fail:bad module";
	}
	css->init(1);
}

parse(s: string): list of (string, list of Decl)
{
	(stylesheet, e) := css->parse(s);
	if (stylesheet == nil) {
		warning("error parsing stylesheet: " + e);
		return nil;
	}
	rules, r: list of (string, list of Decl);
	for (stl := stylesheet.statements; stl != nil; stl = tl stl) {
		pick st := hd stl {
		Ruleset =>
			rules = ruleset2rule(st, rules);
		}
	}
	for (; rules != nil; rules = tl rules)
		r = hd rules :: r;
	return r;
}

ruleset2rule(statement: ref Statement.Ruleset, onto: list of (string, list of Decl)): list of (string, list of Decl)
{
	d := makedecls(statement.decls);
	
	names: list of string;
	for (sels := statement.selectors; sels != nil; sels = tl sels) {
		csel := hd sels;
		if (len csel != 1) {
			warning("context-specific selectors not allowed");
			continue;
		}
		(nil, l) := hd csel;
		if ((name := selector2name(l)) != nil)
			names = name :: names;
	}
	for (; names != nil; names = tl names)
		onto = (hd names, d) :: onto;
	
	return onto;
}

makedecls(decls: list of ref CSS->Decl): list of Decl
{
	d: list of Decl;
	for (; decls != nil; decls = tl decls) {
		nd: Decl;
		nd.name = (hd decls).property;
		nd.important = (hd decls).important;
		s := "";
		for (vals := (hd decls).values; vals != nil; vals = tl vals) {
			vs: string;
			pick v := hd vals {
			Percentage =>
				vs = v.value + "%";
			String or
			Number or
			Url or
			Unicoderange =>
				vs = v.value;
			Hexcolour =>
				vs = rgb2s(v.rgb);
			RGB =>
				vs = rgb2s(v.rgb);
			Ident =>
				vs = v.name;
			Unit =>
				vs = v.value + v.units;
			}
			if (s != nil)
				s[len s] = (hd vals).sep;
			s += vs;
		}
		nd.val = s;
		d = nd :: d;
	}
	return d;
}

rgb2s(rgb: (int, int, int)): string
{
	(r, g, b) := rgb;
	return sys->sprint("#%.2x%.2x%.2x", r, g, b);
}

warning(s: string)
{
	sys->fprint(sys->fildes(2), "cssparser: %s\n", s);
}

selector2name(sel: list of ref Select): string
{
	tag: string;
	class: string;
	pseudo: string;

	for (; sel != nil; sel = tl sel) {
		pick v := hd sel {
		Element =>
			tag = v.name;
		Class =>
			class = "." + v.name;
		Pseudo =>
			class = ":" + v.name;
		* =>
			warning("unknown selector type " + string tagof(hd sel));
		}
	}
	return tag + class + pseudo;
}

parsedecl(s: string): list of Decl
{
	if (s == nil)
		return nil;
	(d, e) := css->parsedecl(s);
	if (d == nil) {
		warning(e);
		return nil;
	}
	return makedecls(d);
}