forsyth20121205-174063a80cc13 years agoHistory
717 lines · 14.5 KB · Brainfuck
RawDownload
implement Xml; ## Portions copyright © 2002 Vita Nuova Holdings Limited### Derived from saxparser.b Copyright © 2001-2002 by John Powers or his employer# # TO DO:# - provide a way of getting attributes out of <?...?> (process) requests,# so that we can process stylesheet requests given in that way. include "sys.m";	sys: Sys;include "bufio.m";	bufio: Bufio;	Iobuf: import bufio;include "string.m";	str: String;include "hash.m";	hash: Hash;	HashTable: import hash;include "xml.m"; Parcel: adt {	pick {	Start or	Empty =>		name: string;		attrs: Attributes;	End =>		name: string;	Text =>		ch: string;		ws1, ws2: int;	Process =>		target: string;		data: string;	Error =>		loc:	Locator;		msg:	string;	Doctype =>		name:	string;		public:	int;		params:	list of string;	Stylesheet =>		attrs: Attributes;	EOF =>	}}; entinit := array[] of {	("AElig", "Æ"),	("OElig", "Œ"),	("aelig", "æ"),	("amp", "&"),	("apos", "\'"),	("copy", "©"),	("gt", ">"),	("ldquo", "``"),	("lt", "<"),	("mdash", "-"),		# XXX ??	("oelig", "œ"),	("quot", "\""),	("rdquo", "''"),	("rsquo", "'"),	("trade", "™"),	("nbsp", "\u00a0"),};entdict: ref HashTable; init(): string{	sys = load Sys Sys->PATH;	bufio = load Bufio Bufio->PATH;	if (bufio == nil)		return sys->sprint("cannot load %s: %r", Bufio->PATH);	str = load String String->PATH;	if (str == nil)		return sys->sprint("cannot load %s: %r", String->PATH);	hash = load Hash Hash->PATH;	if (hash == nil)		return sys->sprint("cannot load %s: %r", Hash->PATH);	entdict = hash->new(23);	for (i := 0; i < len entinit; i += 1) {		(key, value) := entinit[i];		entdict.insert(key, (0, 0.0, value));	}	return nil;} blankparser: Parser; open(srcfile: string, warning: chan of (Locator, string), preelem: string): (ref Parser, string){	fd := bufio->open(srcfile, Bufio->OREAD);	if(fd == nil)		return (nil, sys->sprint("cannot open %s: %r", srcfile));	return fopen(fd, srcfile, warning, preelem);} fopen(fd: ref Bufio->Iobuf, name: string, warning: chan of (Locator, string), preelem: string): (ref Parser, string){	x := ref blankparser;	x.in = fd;	# ignore utf16 initialisation character (yuck)	c := x.in.getc();	if (c != 16rfffe && c != 16rfeff)		x.in.ungetc();	x.estack = nil;	x.loc = Locator(1, name, "");	x.warning = warning;	x.preelem = preelem;	return (x, "");} Parser.next(x: self ref Parser): ref Item{	curroffset := x.fileoffset;	currloc := x.loc;	# read up until end of current item	while (x.actdepth > x.readdepth) {		pick p := getparcel(x) {		Start =>			x.actdepth++;		End =>			x.actdepth--;		EOF =>			x.actdepth = 0;			# premature EOF closes all tags		Error =>			return ref Item.Error(curroffset, x.loc, x.errormsg);		}	}	if (x.actdepth < x.readdepth) {		x.fileoffset = int x.in.offset();		return nil;	}	gp := getparcel(x);	item: ref Item;	pick p := gp {	Start =>		x.actdepth++;		item = ref Item.Tag(curroffset, p.name, p.attrs);	End =>		x.actdepth--;		item = nil;	EOF =>		x.actdepth = 0;		item = nil;	Error =>		x.actdepth = 0;			# XXX is this the right thing to do?		item = ref Item.Error(curroffset, currloc, x.errormsg);	Text =>		item = ref Item.Text(curroffset, p.ch, p.ws1, p.ws2);	Process =>		item = ref Item.Process(curroffset, p.target, p.data);	Empty =>		item = ref Item.Tag(curroffset, p.name, p.attrs);	Doctype =>		item = ref Item.Doctype(curroffset, p.name, p.public, p.params);	Stylesheet =>		item = ref Item.Stylesheet(curroffset, p.attrs);	}	x.fileoffset = int x.in.offset();	return item;} Parser.atmark(x: self ref Parser, m: ref Mark): int{	return  int x.in.offset() == m.offset;} Parser.down(x: self ref Parser){	x.readdepth++;} Parser.up(x: self ref Parser){	x.readdepth--;} # mark is only defined after a next(), not after up() or down().# this means that we don't have to record lots of state when going up or down levels.Parser.mark(x: self ref Parser): ref Mark{	return ref Mark(x.estack, x.loc.line, int x.in.offset(), x.readdepth);} Parser.goto(x: self ref Parser, m: ref Mark){	x.in.seek(big m.offset, Sys->SEEKSTART);	x.fileoffset = m.offset;	x.eof = 0;	x.estack = m.estack;	x.loc.line = m.line;	x.readdepth = m.readdepth;	x.actdepth = len x.estack;} Mark.str(m: self ref Mark): string{	# assume that neither the filename nor any of the tags contain spaces.	# format:	# offset readdepth linenum [tag...]	# XXX would be nice if the produced string did not contain	# any spaces so it could be treated as a word in other contexts.	s := sys->sprint("%d %d %d", m.offset, m.readdepth, m.line);	for (t := m.estack; t != nil; t = tl t)		s += " " + hd t;	return s;} Parser.str2mark(p: self ref Parser, s: string): ref Mark{	(n, toks) := sys->tokenize(s, " ");	if (n < 3)		return nil;	m := ref Mark(nil, p.loc.line, 0, 0);	(m.offset, toks) = (int hd toks, tl toks);	(m.readdepth, toks) = (int hd toks, tl toks);	(m.line, toks) = (int hd toks, tl toks);	m.estack = toks;	return m;} getparcel(x: ref Parser): ref Parcel{	{		p: ref Parcel;		while (!x.eof && p == nil) {			c := getc(x);			if (c == '<')				p = element(x);			else {				ungetc(x);				p = characters(x);			}		}		if (p == nil)			p = ref Parcel.EOF;		return p;	}exception e{	"sax:*" =>			return ref Parcel.Error(x.loc, x.errormsg);	}} parcelstr(gi: ref Parcel): string{	if (gi == nil)		return "nil";	pick i := gi {	Start =>		return sys->sprint("Start: %s", i.name);	Empty =>		return sys->sprint("Empty: %s", i.name);	End =>		return "End";	Text =>		return "Text";	Doctype =>		return sys->sprint("Doctype: %s", i.name);	Stylesheet =>		return "Stylesheet";	Error =>		return "Error: " + i.msg;	EOF =>		return "EOF";	* =>		return "Unknown";	}} element(x: ref Parser): ref Parcel{	# <tag ...>	elemname := xmlname(x);	c: int;	if (elemname != "") {		attrs := buildattrs(x);		skipwhite(x);		c = getc(x);		isend := 0;		if (c == '/')			isend = 1;		else			ungetc(x);		expect(x, '>'); 		if (isend)			return ref Parcel.Empty(elemname, attrs);		else {			startelement(x, elemname);			return ref Parcel.Start(elemname, attrs);		}	# </tag>	} else if ((c = getc(x)) == '/') {		elemname = xmlname(x);		if (elemname != "") {			expect(x, '>');			endelement(x, elemname);			return ref Parcel.End(elemname);		}		else			error(x, sys->sprint("illegal beginning of tag: '%c'", c));	# <?tag ... ?>	} else if (c == '?') {		elemname = xmlname(x);		if (elemname != "") {			# this special case could be generalised if there were many			# processing instructions that took attributes like this.			if (elemname == "xml-stylesheet") {				attrs := buildattrs(x);				balancedstring(x, "?>");				return ref Parcel.Stylesheet(attrs);			} else {				data := balancedstring(x, "?>");				return ref Parcel.Process(elemname, data);			}		}	} else if (c == '!') {		c = getc(x);		case c {		'-' =>			# <!-- comment -->			if(getc(x) == '-'){				balancedstring(x, "-->");				return nil;			}		'[' =>			# <![CDATA[...]]			s := xmlname(x);			if(s == "CDATA" && getc(x) == '['){				data := balancedstring(x, "]]>");				return ref Parcel.Text(data, 0, 0);			}		* =>			# <!declaration			ungetc(x);			s := xmlname(x);			case s {			"DOCTYPE" =>				# <!DOCTYPE name (SYSTEM "filename" | PUBLIC "pubid" "uri"?)? ("[" decls "]")?>				skipwhite(x);				name := xmlname(x);				if(name == nil)					break;				id := "";				uri := "";				public := 0;				skipwhite(x);				case sort := xmlname(x) {				"SYSTEM" =>					id = xmlstring(x, 1);				"PUBLIC" =>					public = 1;					id = xmlstring(x, 1);					skipwhite(x);					c = getc(x);					ungetc(x);					if(c == '"' || c == '\'')						uri = xmlstring(x, 1);				* =>					error(x, sys->sprint("unknown DOCTYPE: %s", sort));					return nil;				}				skipwhite(x);				if(getc(x) == '['){					error(x, "cannot handle DOCTYPE with declarations");					return nil;				}				ungetc(x);				skipwhite(x);				if(getc(x) == '>')					return ref Parcel.Doctype(name, public, id :: uri :: nil);			"ELEMENT" or "ATTRLIST" or "NOTATION" or "ENTITY" =>				# don't interpret internal DTDs				# <!ENTITY name ("value" | SYSTEM "filename")>				s = gets(x, '>');				if(s == nil || s[len s-1] != '>')					error(x, "end of file in declaration");				return nil;			* =>				error(x, sys->sprint("unknown declaration: %s", s));			}		}		error(x, "invalid XML declaration");	} else		error(x, sys->sprint("illegal beginning of tag: %c", c));	return nil;} characters(x: ref Parser): ref Parcel{	p: ref Parcel;	content := gets(x, '<');	if (len content > 0) {		if (content[len content - 1] == '<') {			ungetc(x);			content = content[0:len content - 1];		}		ws1, ws2: int;		if (x.ispre) {			content = substituteentities(x, content);			ws1 = ws2 = 0;		} else			(content, ws1, ws2) = substituteentities_sp(x, content);		if (content != nil || ws1)			p = ref Parcel.Text(content, ws1, ws2);	}	return p;} startelement(x: ref Parser, name: string){	x.estack = name :: x.estack;	if (name == x.preelem)		x.ispre++;} endelement(x: ref Parser, name: string){	if (x.estack != nil && name == hd x.estack) {		x.estack = tl x.estack;		if (name == x.preelem)			x.ispre--;	} else {		starttag := "";		if (x.estack != nil)			starttag = hd x.estack;		warning(x, sys->sprint("<%s></%s> mismatch", starttag, name)); 		# invalid XML but try to recover anyway to reduce turnaround time on fixing errors.		# loop back up through the tag stack to see if there's a matching tag, in which case		# jump up in the stack to that, making some rude assumptions about the		# way Parcels are handled at the top level.		n := 0;		for (t := x.estack; t != nil; (t, n) = (tl t, n + 1))			if (hd t == name)				break;		if (t != nil) {			x.estack = tl t;			x.actdepth -= n;		}	}} buildattrs(x: ref Parser): Attributes{	attrs: list of Attribute; 	attr: Attribute;	for (;;) {		skipwhite(x);		attr.name = xmlname(x);		if (attr.name == nil)			break;		skipwhite(x);		c := getc(x);		if(c != '='){			ungetc(x);			attr.value = nil;		}else			attr.value = xmlstring(x, 1);		attrs = attr :: attrs;	}	return Attributes(attrs);} xmlstring(x: ref Parser, dosub: int): string{	skipwhite(x);	s := "";	delim := getc(x);	if (delim == '\"' || delim == '\'') {		s = gets(x, delim);		n := len s;		if (n == 0 || s[n-1] != delim)			error(x, "unclosed string at end of file");		s = s[0:n-1];	# TO DO: avoid copy		if(dosub)			s = substituteentities(x, s);	} else		error(x, sys->sprint("illegal string delimiter: %c", delim));	return s;} xmlname(x: ref Parser): string{	name := "";	ch := getc(x);	case ch {	'_' or ':' or	'a' to 'z' or	'A' to 'Z' or	16r100 to 16rd7ff or	16re000 or 16rfffd =>		name[0] = ch;

Lines 1–500 of 717.