xref: /inferno-os/appl/lib/w3c/xpointers.b (revision 4eb166cf184c1f102fb79e31b1465ea3e2021c39)
1implement Xpointers;
2
3#
4# Copyright © 2005 Vita Nuova Holdings Limited
5#
6
7include "sys.m";
8	sys: Sys;
9
10include "xpointers.m";
11
12init()
13{
14	sys = load Sys Sys->PATH;
15}
16
17#
18# XPointer framework syntax
19#
20# Pointer ::= Shorthand | SchemeBased
21# Shorthand ::= NCName	# from [XML-Names]
22# SchemeBased ::= PointerPart (S? PointerPart)*
23# PointerPart ::= SchemeName '(' SchemeData ')'
24# SchemeName ::= QName	# from [XML-Names]
25# SchemeData ::= EscapedData*
26# EscapedData ::= NormalChar | '^(' | '^)' | '^^' | '(' SchemeData ')'
27# NormalChar ::= UnicodeChar - [()^]
28# UnicodeChar ::= [#x0 - #x10FFFF]
29
30framework(s: string): (string, list of (string, string, string), string)
31{
32	(q, nm, i) := name(s, 0);
33	if(i >= len s){	# Shorthand
34		if(q != nil)
35			return (nil, nil, "shorthand pointer must be unqualified name");
36		if(nm == nil)
37			return (nil, nil, "missing pointer name");
38		return (nm, nil, nil);
39	}
40	# must be SchemeBased
41	l: list of (string, string, string);
42	for(;;){
43		if(nm == nil){
44			if(q != nil)
45				return (nil, nil, sys->sprint("prefix but no local part in name at %d", i));
46			return (nil, nil, sys->sprint("expected name at %d", i));
47		}
48		if(i >= len s || s[i] != '(')
49			return (nil, nil, sys->sprint("expected '(' at %d", i));
50		o := i++;
51		a := "";
52		nesting := 0;
53		for(; i < len s && ((c := s[i]) != ')' || nesting); i++){
54			case c {
55			'^' =>
56				if(i+1 >= len s)
57					return (nil, nil, "unexpected eof after ^");
58				c = s[++i];
59				if(c != '(' && c != ')' && c != '^')
60					return (nil, nil, sys->sprint("invalid escape ^%c at %d", c, i));
61			'(' =>
62				nesting++;
63			')' =>
64				if(--nesting < 0)
65					return (nil, nil, sys->sprint("unbalanced ) at %d", i));
66			}
67			a[len a] = c;
68		}
69		if(i >= len s)
70			return (nil, nil, sys->sprint("unbalanced ( at %d", o));
71		l = (q, nm, a) :: l;
72		if(++i == len s)
73			break;
74		while(i < len s && isspace(s[i]))
75			i++;
76		(q, nm, i) = name(s, i);
77	}
78	rl: list of (string, string, string);
79	for(; l != nil; l = tl l)
80		rl = hd l :: rl;
81	return (nil, rl, nil);
82}
83
84isspace(c: int): int
85{
86	return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f';
87}
88
89#
90# QName ::= (Prefix ':')? LocalPart
91# Prefix ::= NCName
92# LocalPart ::= NCName
93#
94#NCName :: (Oetter | '_') NCNameChar*
95#NCNameChar :: Oetter | Digit | '.' | '-' | '_' | CombiningChar | Extender
96
97name(s: string, o: int): (string, string, int)
98{
99	(ns, i) := ncname(s, o);
100	if(i >= len s || s[i] != ':')
101		return (nil, ns, i);
102	(nm, j) := ncname(s, i+1);
103	if(j == i+1)
104		return (nil, ns, i);	# assume it's a LocalPart followed by ':'
105	return (ns, nm, j);
106}
107
108ncname(s: string, o: int): (string, int)
109{
110	if(o >= len s || !isalnum(c := s[o]) && c != '_' || c >= '0' && c <= '9')
111		return (nil, o);	# missing or invalid start character
112	for(i := o; i < len s && isnamec(s[i]); i++)
113		;
114	return (s[o:i], i);
115}
116
117isnamec(c: int): int
118{
119	return isalnum(c) || c == '_' || c == '-' || c == '.';
120}
121
122isalnum(c: int): int
123{
124	#
125	# Hard to get absolutely right without silly amount of character data.
126	# Use what we know about ASCII
127	# and assume anything above the Oatin control characters is
128	# potentially an alphanumeric.
129	#
130	if(c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9')
131		return 1;	# usual case
132	if(c <= ' ')
133		return 0;
134	if(c > 16rA0)
135		return 1;	# non-ASCII
136	return 0;
137}
138
139# schemes: xpointer(), xmlns(), element()
140
141# xmlns()
142#	XmlnsSchemeData ::= NCName S? '=' S? EscapedNamespaceName
143#	EscapedNamespaceName ::= EscapedData*
144
145xmlns(s: string): (string, string, string)
146{
147	(nm, i) := ncname(s, 0);
148	if(nm == nil)
149		return (nil, nil, "illegal namespace name");
150	while(i < len s && isspace(s[i]))
151		i++;
152	if(i >= len s || s[i++] != '=')
153		return (nil, nil, "illegal xmlns declaration");
154	while(i < len s && isspace(s[i]))
155		i++;
156	return (nm, s[i:], nil);
157}
158
159# element()
160#	ElementSchemeData ::= (NCName ChildSequence?) | ChildSequence
161#	ChildSequence ::= ('/' [1-9] [0-9]*)+
162
163element(s: string): (string, list of int, string)
164{
165	nm: string;
166	i := 0;
167	if(s != nil && s[0] != '/'){
168		(nm, i) = ncname(s, 0);
169		if(nm == nil)
170			return (nil, nil, "illegal element name");
171	}
172	l: list of int;
173	do{
174		if(i >= len s || s[i++] != '/')
175			return (nil, nil, "illegal child sequence (expected '/')");
176		v := 0;
177		do{
178			if(i >= len s || !isdigit(s[i]))
179				return (nil, nil, "illegal child sequence (expected integer)");
180			v = v*10 + s[i]-'0';
181		}while(++i < len s && s[i] != '/');
182		l = v :: l;
183	}while(i < len s);
184	rl: list of int;
185	for(; l != nil; l = tl l)
186		rl = hd l :: rl;
187	return (nm, rl, nil);
188}
189
190# xpointer()
191#	XpointerSchemeData ::= Expr	# from Xpath, with new functions and data types
192
193xpointer(s: string): (ref Xpath, string)
194{
195	p := ref Parse(ref Rd(s, 0, 0), nil);
196	{
197		e := expr(p, 0);
198		if(p.r.i < len s)
199			synerr("missing operator");
200		return (e, nil);
201	}exception e{
202	"syntax error*" =>
203		return (nil, e);
204	* =>
205		raise;
206	}
207}
208
209Lerror, Ldslash, Lint, Lreal, Llit, Lvar, Ldotdot, Lop, Laxis, Lfn: con 'a'+iota;	# internal lexical items
210
211Keywd: adt {
212	name:	string;
213	val:	int;
214};
215
216axes: array of Keywd = array[] of {
217	("ancestor", Aancestor),
218	("ancestor-or-self", Aancestor_or_self),
219	("attribute", Aattribute),
220	("child", Achild),
221	("descendant", Adescendant),
222	("descendant-or-self", Adescendant_or_self),
223	("following", Afollowing),
224	("following-sibling", Afollowing_sibling),
225	("namespace", Anamespace),
226	("parent", Aparent),
227	("preceding", Apreceding),
228	("preceding-sibling", Apreceding_sibling),
229	("self", Aself),
230};
231
232keywds: array of Keywd = array[] of {
233	("and", Oand),
234	("comment", Onodetype),
235	("div", Odiv),
236	("mod", Omod),
237	("node", Onodetype),
238	("or", Oor),
239	("processing-instruction", Onodetype),
240	("text", Onodetype),
241};
242
243iskeywd(s: string): int
244{
245	return look(keywds, s);
246}
247
248look(k: array of Keywd, s: string): int
249{
250	for(i := 0; i < len k; i++)
251		if(k[i].name == s)
252			return k[i].val;
253	return 0;
254}
255
256lookname(k: array of Keywd, v: int): string
257{
258	for(i := 0; i < len k; i++)
259		if(k[i].val == v)
260			return k[i].name;
261	return nil;
262}
263
264prectab := array[] of {
265	array[] of {Oor},
266	array[] of {Oand},
267	array[] of {'=', One},
268	array[] of {'<', Ole, '>', Oge},
269	array[] of {'+', '-'},
270	array[] of {Omul, Odiv, Omod},
271	array[] of {Oneg},	# unary '-'
272	array[] of {'|'},	# UnionExpr
273};
274
275isop(t: int, p: array of int): int
276{
277	if(t >= 0)
278		for(j := 0; j < len p; j++)
279			if(t == p[j])
280				return 1;
281	return 0;
282}
283
284# Expr ::= OrExpr
285# UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
286# PathExpr ::= LocationPath | FilterExpr | FilterExpr '/' RelativeLocationPath |
287#			FilterExpr '//' RelativeLocationPath
288# OrExpr ::= AndExpr | OrExpr 'or' AndExpr
289# AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
290# EqualityExpr ::= RelationalExpr | EqualityExpr '=' RelationalExpr | EqualityExpr '!=' RelationalExpr
291# RelationalExpr ::= AdditiveExpr | RelationalExpr '<' AdditiveExpr | RelationalExpr '>' AdditiveExpr |
292#				RelationalExpr '<=' AdditiveExpr | RelationalExpr '>=' AdditiveExpr
293# AdditiveExpr ::= MultiplicativeExpr | AdditiveExpr '+' MultiplicativeExpr | AdditiveExpr '-' MultiplicativeExpr
294# MultiplicativeExpr ::= UnaryExpr | MultiplicativeExpr MultiplyOperator UnaryExpr |
295#				MultiplicativeExpr 'div' UnaryExpr | MultiplicativeExpr 'mod' UnaryExpr
296# UnaryExpr ::= UnionExpr | '-' UnaryExpr
297
298expr(p: ref Parse, k: int): ref Xpath
299{
300	if(k >= len prectab)
301		return pathexpr(p);
302	if(prectab[k][0] == Oneg){	# unary '-'
303		if(p.look() == '-'){
304			p.get();
305			return ref Xpath.E(Oneg, expr(p,k+1), nil);
306		}
307		# must be UnionExpr
308		k++;
309	}
310	e := expr(p, k+1);
311	while(isop(p.look(), prectab[k])){
312		o := p.get().t0;
313		e = ref Xpath.E(o, e, expr(p, k+1));	# +assoc[k]
314	}
315	return e;
316}
317
318# PathExpr ::= LocationPath | FilterExpr ( ('/' | '//') RelativeLocationPath )
319# FilterExpr ::= PrimaryExpr | FilterExpr Predicate => PrimaryExpr Predicate*
320
321pathexpr(p: ref Parse): ref Xpath
322{
323	# LocationPath?
324	case p.look() {
325	'.' or Ldotdot or Laxis or '@' or Onametest or Onodetype or '*' =>
326		return locationpath(p, 0);
327	'/' or Ldslash =>
328		return locationpath(p, 1);
329	}
330	# FilterExpr
331	e := primary(p);
332	while(p.look() == '[')
333		e = ref Xpath.E(Ofilter, e, predicate(p));
334	if((o := p.look()) == '/' || o == Ldslash)
335		e = ref Xpath.E(Opath, e, locationpath(p, 0));
336	return e;
337}
338
339# LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
340# AbsoluteLocationPath ::= '/' RelativeLocationPath? | AbbreviatedAbsoluteLocationPath
341# RelativeLocationPath ::= Step | RelativeLocationPath '/' Step
342# AbbreviatedAbsoluteLocationPath ::= '//' RelativeLocationPath
343# AbbreviatedRelativeLocationPath ::= RelativeLocationPath '//' Step
344
345locationpath(p: ref Parse, abs: int): ref Xpath
346{
347	# // => /descendent-or-self::node()/
348	pl: list of ref Xstep;
349	o := p.look();
350	if(o != '/' && o != Ldslash){
351		s := step(p);
352		if(s == nil)
353			synerr("expected Step in LocationPath");
354		pl = s :: pl;
355	}
356	while((o = p.look()) == '/' || o == Ldslash){
357		p.get();
358		if(o == Ldslash)
359			pl = ref Xstep(Adescendant_or_self, Onodetype, nil, "node", nil, nil) :: pl;
360		s := step(p);
361		if(s == nil){
362			if(abs && pl == nil)
363				break;	# it's just an initial '/'
364			synerr("expected Step in LocationPath");
365		}
366		pl = s :: pl;
367	}
368	return ref Xpath.Path(abs, rev(pl));
369}
370
371# Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep
372# AxisSpecifier ::= AxisName '::' | AbbreviatedAxisSpecifier
373# AxisName := ... # long list
374# NodeTest ::= NameTest | NodeType '(' ')'
375# Predicate ::= '[' PredicateExpr ']'
376# PredicateExpr ::= Expr
377# AbbreviatedStep ::= '.' | '..'
378# AbbreviatedAxisSpecifier ::= '@'?
379
380step(p: ref Parse): ref Xstep
381{
382	# AxisSpecifier ... | AbbreviatedStep
383	(o, ns, nm) := p.get();
384	axis := Achild;
385	case o {
386	'.' =>
387		return ref Xstep(Aself, Onodetype, nil, "node", nil, nil);	# self::node()
388	Ldotdot =>
389		return ref Xstep(Aparent, Onodetype, nil, "node", nil, nil);	# parent::node()
390	Laxis =>
391		axis = look(axes, ns);
392		(o, ns, nm) = p.get();
393	'@' =>
394		axis = Aattribute;
395		(o, ns, nm) = p.get();
396	* =>
397		;
398	}
399
400	if(o == '*'){
401		o = Onametest;
402		nm = "*";
403		ns = nil;
404	}
405
406	# NodeTest ::= NameTest | NodeType '(' ')'
407	if(o != Onametest && o != Onodetype){
408		p.unget((o, ns, nm));
409		return nil;
410	}
411
412	arg: string;
413	if(o == Onodetype){	# '(' ... ')'
414		expect(p, '(');
415		# grammar is wrong: processing-instruction can have optional literal
416		if(nm == "processing-instruction" && p.look() == Llit)
417			arg = p.get().t1;
418		expect(p, ')');
419	}
420
421	# Predicate*
422	pl: list of ref Xpath;
423	while((pe := predicate(p)) != nil)
424		pl = pe :: pl;
425	return ref Xstep(axis, o, ns, nm, arg, rev(pl));
426}
427
428# PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall
429# FunctionCall ::= FunctionName '(' (Argument ( ',' Argument)*)? ')'
430# Argument ::= Expr
431
432primary(p: ref Parse): ref Xpath
433{
434	(o, ns, nm) := p.get();
435	case o {
436	Lvar =>
437		return ref Xpath.Var(ns, nm);
438	'(' =>
439		e := expr(p, 0);
440		expect(p, ')');
441		return e;
442	Llit =>
443		return ref Xpath.Str(ns);
444	Lint =>
445		return ref Xpath.Int(big ns);
446	Lreal =>
447		return ref Xpath.Real(real ns);
448	Lfn =>
449		expect(p, '(');
450		al: list of ref Xpath;
451		if(p.look() != ')'){
452			for(;;){
453				al = expr(p, 0) :: al;
454				if(p.look() != ',')
455					break;
456				p.get();
457			}
458			al = rev(al);
459		}
460		expect(p, ')');
461		return ref Xpath.Fn(ns, nm, al);
462	* =>
463		synerr("invalid PrimaryExpr");
464		return nil;
465	}
466}
467
468# Predicate ::= '[' PredicateExpr ']'
469# PredicateExpr ::= Expr
470
471predicate(p: ref Parse): ref Xpath
472{
473	l := p.get();
474	if(l.t0 != '['){
475		p.unget(l);
476		return nil;
477	}
478	e := expr(p, 0);
479	expect(p, ']');
480	return e;
481}
482
483expect(p: ref Parse, t: int)
484{
485	l := p.get();
486	if(l.t0 != t)
487		synerr(sys->sprint("expected '%c'", t));
488}
489
490Xpath.text(e: self ref Xpath): string
491{
492	if(e == nil)
493		return "nil";
494	pick r := e {
495	E =>
496		if(r.r == nil)
497			return sys->sprint("(%s%s)", opname(r.op), r.l.text());
498		if(r.op == Ofilter)
499			return sys->sprint("%s[%s]", r.l.text(), r.r.text());
500		return sys->sprint("(%s%s%s)", r.l.text(), opname(r.op), r.r.text());
501	Fn =>
502		a := "";
503		for(l := r.args; l != nil; l = tl l)
504			a += sys->sprint(",%s", (hd l).text());
505		if(a != "")
506			a = a[1:];
507		return sys->sprint("%s(%s)", qual(r.ns, r.name), a);
508	Var =>
509		return sys->sprint("$%s", qual(r.ns, r.name));
510	Path =>
511		if(r.abs)
512			t := "/";
513		else
514			t = "";
515		for(l := r.steps; l != nil; l = tl l){
516			if(t != nil && t != "/")
517				t += "/";
518			t += (hd l).text();
519		}
520		return t;
521	Int =>
522		return sys->sprint("%bd", r.val);
523	Real =>
524		return sys->sprint("%g", r.val);
525	Str =>
526		return sys->sprint("%s", str(r.s));
527	}
528}
529
530qual(ns: string, nm: string): string
531{
532	if(ns != nil)
533		return ns+":"+nm;
534	return nm;
535}
536
537str(s: string): string
538{
539	for(i := 0; i < len s; i++)
540		if(s[i] == '\'')
541			return sys->sprint("\"%s\"", s);
542	return sys->sprint("'%s'", s);
543}
544
545opname(o: int): string
546{
547	case o {
548	One =>	return "!=";
549	Ole =>	return "<=";
550	Oge =>	return ">=";
551	Omul =>	return "*";
552	Odiv =>	return " div ";
553	Omod =>	return " mod ";
554	Oand =>	return " and ";
555	Oor =>	return " or ";
556	Oneg =>	return "-";
557	Ofilter =>	return " op_filter ";
558	Opath =>	return "/";
559	* =>	return sys->sprint(" %c ", o);
560	}
561}
562
563Xstep.text(s: self ref Xstep): string
564{
565	t := sys->sprint("%s::", Xstep.axisname(s.axis));
566	case s.op {
567	Onametest =>
568		if(s.ns == "*" && s.name == "*")
569			t += "*";
570		else
571			t += qual(s.ns, s.name);
572	Onodetype =>
573		if(s.arg != nil)
574			t += sys->sprint("%s(%s)", s.name, str(s.arg));
575		else
576			t += sys->sprint("%s()", s.name);
577	}
578	for(l := s.preds; l != nil; l = tl l)
579		t += sys->sprint("[%s]", (hd l).text());
580	return t;
581}
582
583Xstep.axisname(n: int): string
584{
585	return lookname(axes, n);
586}
587
588# ExprToken ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::' |
589#				NameTest | NodeType | Operator | FunctionName | AxisName |
590#				Literal | Number | VariableReference
591# Operator ::= OperatorName | MultiplyOperator | '/' | '//' | '|' | '+' | '' | '=' | '!=' | '<' | '<=' | '>' | '>='
592# MultiplyOperator ::= '*'
593# FunctionName ::= QName - NodeType
594# VariableReference ::= '$' QName
595# NameTest ::= '*' | NCName ':' '*' | QName
596# NodeType ::= 'comment' | 'text' | 'processing-instruction' | 'node'
597#
598
599Lex: type (int, string, string);
600
601Parse: adt {
602	r:	ref Rd;
603	pb:	list of Lex;	# push back
604
605	look:	fn(p: self ref Parse): int;
606	get:	fn(p: self ref Parse): Lex;
607	unget:	fn(p: self ref Parse, t: Lex);
608};
609
610Parse.get(p: self ref Parse): Lex
611{
612	if(p.pb != nil){
613		h := hd p.pb;
614		p.pb = tl p.pb;
615		return h;
616	}
617	return lex(p.r);
618}
619
620Parse.look(p: self ref Parse): int
621{
622	t := p.get();
623	p.unget(t);
624	return t.t0;
625}
626
627Parse.unget(p: self ref Parse, t: Lex)
628{
629	p.pb = t :: p.pb;
630}
631
632lex(r: ref Rd): Lex
633{
634	l := lex0(r);
635	r.prev = l.t0;
636	return l;
637}
638
639# disambiguating rules are D1 to D3
640
641# D1. preceding token p && p not in {'@', '::', '(', '[', ',', Operator} then '*' is MultiplyOperator
642#     and NCName must be OperatorName
643
644xop(t: int): int
645{
646	case t {
647	-1 or 0 or '@' or '(' or '[' or ',' or Lop or Omul or
648	'/' or Ldslash or '|' or '+' or '-' or '=' or One or '<' or Ole or '>' or Oge or
649	Oand or Oor or Omod or Odiv or Laxis =>
650		return 0;
651	}
652	return 1;
653}
654
655# UnaryExpr ::= UnionExpr | '-' UnaryExpr
656# ExprToken ::= ... |
657#				NameTest | NodeType | Operator | FunctionName | AxisName |
658#				Literal | Number | VariableReference
659# Operator ::= OperatorName | MultiplyOperator | '/' | '//' | '|' | '+' | '' | '=' | '!=' | '<' | '<=' | '>' | '>='
660# MultiplyOperator ::= '*'
661
662lex0(r: ref Rd): Lex
663{
664	while(isspace(r.look()))
665		r.get();
666	case c := r.get() {
667	-1 or
668	'(' or ')' or '[' or ']' or '@' or ',' or '+' or '-' or '|' or '=' or ':' =>
669		# singletons ('::' only valid after name, see below)
670		return (c, nil, nil);
671	'/' =>
672		return subseq(r, '/', Ldslash, '/');
673	'!' =>
674		return subseq(r, '=', One, '!');
675	'<' =>
676		return subseq(r, '=', Ole, '<');
677	'>' =>
678		return subseq(r, '=', Oge, '>');
679	'*' =>
680		if(xop(r.prev))
681			return (Omul, nil, nil);
682		return (c, nil, nil);
683	'.' =>
684		case r.look() {
685		'0' to '9' =>
686			(v, nil) := number(r, r.get());
687			return (Lreal, v, nil);
688		'.' =>
689			r.get();
690			return (Ldotdot, nil, nil);
691		* =>
692			return ('.', nil, nil);
693		}
694	'$' =>
695		# variable reference
696		(ns, nm, i) := name(r.s, r.i);
697		if(ns == nil && nm == nil)
698			return (Lerror, nil, nil);
699		r.i = i;
700		return (Lvar, ns, nm);
701	'0' to '9' =>
702		(v, f) := number(r, c);
703		if(f)
704			return (Lreal, v, nil);
705		return (Lint, v, nil);
706	'"' or '\'' =>
707		return (Llit, literal(r, c), nil);
708	* =>
709		if(isalnum(c) || c == '_'){
710			# QName/NCName
711			r.unget();
712			(ns, nm, i) := name(r.s, r.i);
713			if(ns == nil && nm == nil)
714				return (Lerror, nil, nil);
715			r.i = i;
716			if(xop(r.prev)){
717				if(ns == nil){
718					o := iskeywd(nm);
719					if(o != Laxis && o != Onodetype)
720						return (o, nil, nil);
721				}
722				return (Lop, ns, nm);
723			}
724			while(isspace(r.look()))
725				r.get();
726			case r.look() {
727			'(' =>		# D2: NCName '(' =>NodeType or FunctionName
728				if(ns == nil && iskeywd(nm) == Onodetype)
729					return (Onodetype, nil, nm);
730				return (Lfn, ns, nm);	# possibly NodeTest
731			':' =>		# D3: NCName '::' => AxisName
732				r.get();
733				case r.look() {
734				':' =>
735					if(ns == nil && look(axes, nm) != 0){
736						r.get();
737						return (Laxis, nm, nil);
738					}
739				'*' =>
740					# NameTest ::= ... | NCName ':' '*'
741					if(ns == nil){
742						r.get();
743						return (Onametest, nm, "*");
744					}
745				}
746				r.unget();	# put back the ':'
747				# NameTest ::= '*' | NCName ':' '*' | QName
748			}
749			return (Onametest, ns, nm);	# actually NameTest
750		}
751		# unexpected character
752	}
753	return (Lerror, nil, nil);
754}
755
756subseq(r: ref Rd, a: int, t: int, e: int): Lex
757{
758	if(r.look() != a)
759		return (e, nil, nil);
760	r.get();
761	return (t, nil, nil);
762}
763
764# Literal ::= '"'[^"]*'"' | "'"[^']* "'"
765
766literal(r: ref Rd, delim: int): string
767{
768	s: string;
769	while((c := r.get()) != delim){
770		if(c < 0){
771			synerr("missing string terminator");
772			return s;
773		}
774		if(c)
775			s[len s] = c;	# could slice r.s
776	}
777	return s;
778}
779
780#
781# Number ::= Digits('.' Digits?)? | '.' Digits
782# Digits ::= [0-9]+
783#
784number(r: ref Rd, c: int): (string, int)
785{
786	s: string;
787	for(; isdigit(c); c = r.get())
788		s[len s] = c;
789	if(c != '.'){
790		if(c >= 0)
791			r.unget();
792		return (s, 0);
793	}
794	if(!isdigit(c = r.get())){
795		if(c >= 0)
796			r.unget();
797		r.unget();	# the '.'
798		return (s, 0);
799	}
800	s[len s] = '.';
801	do{
802		s[len s] = c;
803	}while(isdigit(c = r.get()));
804	if(c >= 0)
805		r.unget();
806	return (s, 1);
807}
808
809isdigit(c: int): int
810{
811	return c>='0' && c<='9';
812}
813
814Rd: adt{
815	s:	string;
816	i:	int;
817	prev:	int;	# previous token
818
819	get:	fn(r: self ref Rd): int;
820	look:	fn(r: self ref Rd): int;
821	unget:	fn(r: self ref Rd);
822};
823
824Rd.get(r: self ref Rd): int
825{
826	if(r.i >= len r.s)
827		return -1;
828	return r.s[r.i++];
829}
830
831Rd.look(r: self ref Rd): int
832{
833	if(r.i >= len r.s)
834		return -1;
835	return r.s[r.i];
836}
837
838Rd.unget(r: self ref Rd)
839{
840	if(r.i > 0)
841		r.i--;
842}
843
844rev[T](l: list of T): list of T
845{
846	rl: list of T;
847	for(; l != nil; l = tl l)
848		rl = hd l :: rl;
849	return rl;
850}
851
852synerr(s: string)
853{
854	raise "syntax error: "+s;
855}
856
857# to do:
858#	dictionary?
859