|
|
1.1 root 1: # RSG
2: #
3: # Random sentence generation
4: #
5: # Ralph E. Griswold
6: #
7: # Last modified 4/29/86
8: #
9:
10: global defs, ifile, in, limit, tswitch, prompt
11:
12: record nonterm(name)
13: record charset(chars)
14: record query(name)
15:
16: procedure main(x)
17: local line, plist, i, s
18: # procedures to try on input lines
19: plist := [define,generate,grammar,source,comment,prompter,error]
20: defs := table() # table of definitions
21: defs["lb"] := [["<"]] # built-in definitions
22: defs["rb"] := [[">"]]
23: defs["vb"] := [["|"]]
24: defs["nl"] := [["\n"]]
25: defs[""] := [[""]]
26: defs["&lcase"] := [[charset(&lcase)]]
27: defs["&ucase"] := [[charset(&ucase)]]
28: defs["&digit"] := [[charset('0123456789')]]
29: i := 0
30: while i < *x do { # process options
31: s := x[i +:= 1] | break
32: case s of {
33: "-t": tswitch := 1
34: "-l": limit := integer(x[i +:= 1]) | Usage()
35: "-s": &random := integer(x[i +:= 1]) | Usage()
36: default: Usage()
37: }
38: }
39: ifile := [&input] # stack of input files
40: prompt := ""
41: while in := pop(ifile) do { # process all files
42: repeat {
43: if *prompt ~= 0 then writes(prompt)
44: line := read(in) | break
45: while line[-1] == "\\" do line := line[1:-1] || read(in) | break
46: (!plist)(line)
47: }
48: close(in)
49: }
50: end
51:
52: # process alternatives
53: #
54: procedure alts(defn)
55: local alist
56: static nonbar
57: initial nonbar := ~'|'
58: alist := []
59: defn ? while put(alist,syms(tab(many(nonbar)))) do move(1)
60: return alist
61: end
62:
63: # look for comment
64: #
65: procedure comment(line)
66: if line[1] == "#" then return
67: end
68:
69: # look for definition
70: #
71: procedure define(line)
72: return line ?
73: defs[(="<",tab(find(">::=")))] := (move(4),alts(tab(0)))
74: end
75:
76: # define nonterminal
77: #
78: procedure defnon(sym)
79: local chars, name
80: if sym ? {
81: ="'" &
82: chars := cset(tab(-1)) &
83: ="'"
84: }
85: then return charset(chars)
86: else if sym ? {
87: ="?" &
88: name := tab(0)
89: }
90: then return query(name)
91: else return nonterm(sym)
92: end
93:
94: # note erroneous input line
95: #
96: procedure error(line)
97: write("*** erroneous line: ",line)
98: return
99: end
100:
101: # generate sentences
102: #
103: procedure gener(goal)
104: local pending, genstr, symbol
105: repeat {
106: pending := [nonterm(goal)]
107: genstr := ""
108: while symbol := get(pending) do {
109: if \tswitch then
110: write(&errout,genstr,symimage(symbol),listimage(pending))
111: case type(symbol) of {
112: "string": genstr ||:= symbol
113: "charset": genstr ||:= ?symbol.chars
114: "query": {
115: writes("*** supply string for ",symbol.name," ")
116: genstr ||:= read() | {
117: write(&errout,"*** no value for query to ",symbol.name)
118: suspend genstr
119: break next
120: }
121: }
122: "nonterm": {
123: pending := ?\defs[symbol.name] ||| pending | {
124: write(&errout,"*** undefined nonterminal: <",symbol.name,">")
125: suspend genstr
126: break next
127: }
128: if *pending > \limit then {
129: write(&errout,"*** excessive symbols remaining")
130: suspend genstr
131: break next
132: }
133: }
134: }
135: }
136: suspend genstr
137: }
138: end
139:
140: # look for generation specification
141: #
142: procedure generate(line)
143: local goal, count
144: if line ? {
145: ="<" &
146: goal := tab(upto('>')) \ 1 &
147: move(1) &
148: count := (pos(0) & 1) | integer(tab(0))
149: }
150: then {
151: every write(gener(goal)) \ count
152: return
153: }
154: else fail
155: end
156:
157: # get right hand side of production
158: #
159: procedure getrhs(a)
160: local rhs
161: rhs := ""
162: every rhs ||:= listimage(!a) || "|"
163: return rhs[1:-1]
164: end
165:
166: # look for request to write out grammar
167: #
168: procedure grammar(line)
169: local file, out, name
170: if line ? {
171: name := tab(find("->")) &
172: move(2) &
173: file := tab(0) &
174: out := if *file = 0 then &output else {
175: open(file,"w") | {
176: write(&errout,"*** cannot open ",file)
177: fail
178: }
179: }
180: }
181: then {
182: (*name = 0) | (name[1] == "<" & name[-1] == ">") | fail
183: pwrite(name,out)
184: if *file ~= 0 then close(out)
185: return
186: }
187: else fail
188: end
189:
190: # produce image of list of grammar symbols
191: #
192: procedure listimage(a)
193: local s, x
194: s := ""
195: every x := !a do
196: s ||:= symimage(x)
197: return s
198: end
199:
200: # look for new prompt symbol
201: #
202: procedure prompter(line)
203: if line[1] == "=" then {
204: prompt := line[2:0]
205: return
206: }
207: end
208:
209: # write out grammar
210: #
211: procedure pwrite(name,ofile)
212: local nt, a
213: static builtin
214: initial builtin := ["lb","rb","vb","nl","","&lcase","&ucase","&digit"]
215: if *name = 0 then {
216: a := sort(defs,3)
217: while nt := get(a) do {
218: if nt == !builtin then {
219: get(a)
220: next
221: }
222: write(ofile,"<",nt,">::=",getrhs(get(a)))
223: }
224: }
225: else write(ofile,name,"::=",getrhs(\defs[name[2:-1]])) |
226: write("*** undefined nonterminal: ",name)
227: end
228:
229: # look for file with input
230: #
231: procedure source(line)
232: local file
233: return line ? (="@" & push(ifile,in) & {
234: in := open(file := tab(0)) | {
235: write(&errout,"*** cannot open ",file)
236: fail
237: }
238: })
239: end
240:
241: # produce string image of grammar symbol
242: #
243: procedure symimage(x)
244: return case type(x) of {
245: "string": x
246: "nonterm": "<" || x.name || ">"
247: "charset": "<'" || x.chars || "'>"
248: }
249: end
250:
251: # process the symbols in an alternative
252: #
253: procedure syms(alt)
254: local slist
255: static nonbrack
256: initial nonbrack := ~'<'
257: slist := []
258: alt ? while put(slist,tab(many(nonbrack)) |
259: defnon(2(="<",tab(upto('>')),move(1))))
260: return slist
261: end
262:
263: # stop, noting incorrect usage
264: #
265: procedure Usage()
266: stop("usage: [-t] [-l n] [-s n]")
267: end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.