1/* "CodeWorker":	a scripting language for parsing and generating text.
2
3Copyright (C) 1996-1997, 1999-2002 C�dric Lemaire
4
5This library is free software; you can redistribute it and/or
6modify it under the terms of the GNU Lesser General Public
7License as published by the Free Software Foundation; either
8version 2.1 of the License, or (at your option) any later version.
9
10This library is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13Lesser General Public License for more details.
14
15You should have received a copy of the GNU Lesser General Public
16License along with this library; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
19To contact the author: codeworker@free.fr
20*/
21
22function checkType(sType : value) {
23	switch(sType) {
24		case "int":
25		case "bool":
26		case "double":
27		case "doubleref":
28		case "boolref":
29		case "iterator":
30		case "script":
31		case "string":
32		case "stringref":
33		case "stringlist":
34		case "treeref":
35		case "tree":
36		case "treexpr":
37		case "ulong":
38		case "ushort":
39			return "true";
40		default:
41			return "";
42	}
43}
44
45function parseType(myType : node) {
46	local sType = readIdentifier();
47	if !sType error("syntax error: type expected, instead of '" + readChar() + "'");
48	if !sType.checkType() error("unrecognized type '" + sType + "' found");
49	if sType == "script" {
50		skipEmptyCpp();
51		if !readIfEqualTo('<') error("'<' expected after 'script' type");
52		skipEmptyCpp();
53		set sType = readIdentifier();
54		switch(sType) {
55			case "BNF":
56			case "free":
57			case "pattern":
58			case "translate":
59				insert myType.script = sType;
60				break;
61			default:
62				error("'script<" + sType + ">' doesn't exist as a type");
63		}
64		skipEmptyCpp();
65		if !readIfEqualTo('>') error("'>' expected to close 'script' type");
66		set myType = "script";
67	} else {
68		set myType = sType;
69	}
70}
71
72function parseMemberDocumentation(myMember : node, sMemberName, sMemberType) {
73	nop(skipEmptyCpp());
74	if !readIfEqualTo("=") error("syntax error: '=' expected to provide documentation of " + sMemberType + " '" + sMemberName + "'");
75	nop(skipEmptyCpp());
76	local sDocumentation;
77	if !readString(sDocumentation) error("syntax error: documentation of " + sMemberType + " '" + sMemberName + "' expected between quotes");
78	local sText;
79	while skipEmptyCpp() && readString(sText) set sDocumentation += endl() + sText;
80	insert myMember.documentation = sDocumentation;
81	nop(skipEmptyCpp());
82	if !readIfEqualToIdentifier("heading") error("'heading' keyword required after the definition of '" + sMemberName + "'");
83	nop(skipEmptyCpp());
84	local sIdentifier;
85	if readString(sIdentifier) insert myMember.abstract = sIdentifier;
86	do {
87		nop(skipEmptyCpp());
88		set sIdentifier = readIdentifier();
89		if !sIdentifier error("heading name expected");
90		switch(sIdentifier) {
91			case "array":
92			case "command":
93			case "conversion":
94			case "datetime":
95			case "directory":
96			case "file":
97			case "generation":
98			case "iterator":
99			case "interpreter":
100			case "node":
101			case "numeric":
102			case "parsing":
103			case "socket":
104			case "standard":
105			case "string":
106			case "system":
107			case "unknown":
108			case "URL":
109				insert myMember.headings[sIdentifier] = sIdentifier;
110				ref project.headings[sIdentifier].members[sMemberName] = myMember;
111				insert project.headings[sIdentifier].modes[myMember.mode];
112		}
113		nop(skipEmptyCpp());
114	} while readIfEqualTo(',');
115	if readIfEqualToIdentifier("input") {
116		nop(skipEmptyCpp());
117		local sInput;
118		if !readString(sInput) error("syntax error: input of " + sMemberType + " '" + sMemberName + "' expected between quotes");
119		while skipEmptyCpp() && readString(sText) set sInput += endl() + sText;
120		insert myMember.input = sInput;
121		nop(skipEmptyCpp());
122	}
123	if readIfEqualToIdentifier("example") {
124		nop(skipEmptyCpp());
125		if readIfEqualToIdentifier("new_project") {
126			insert myMember.example.newProject = true;
127			nop(skipEmptyCpp());
128		}
129		if readIfEqualToIdentifier("expand") {
130			insert myMember.example.expand = true;
131			nop(skipEmptyCpp());
132		}
133		if readIfEqualToIdentifier("standard_input") {
134			nop(skipEmptyCpp());
135			if !readIfEqualTo("(") error("'(' expected after 'standard_input'");
136			nop(skipEmptyCpp());
137			if !readString(myMember.example.standardInput) error("'standard_input' expects the keyboard input");
138			nop(skipEmptyCpp());
139			if !readIfEqualTo(")") error("')' expected after 'standard_input'");
140			nop(skipEmptyCpp());
141		}
142		local sExample;
143		if !readString(sExample) error("syntax error: example of " + sMemberType + " '" + sMemberName + "' expected between quotes");
144		while skipEmptyCpp() && readString(sText) set sExample += endl() + sText;
145		insert myMember.example = sExample;
146		nop(skipEmptyCpp());
147	}
148	if readIfEqualToIdentifier("deprecated") {
149		nop(skipEmptyCpp());
150		local sKeyword = readIdentifier();
151		if !sKeyword error("keyword expected for 'deprecated' on " + sMemberType + " '" + sMemberName + "'");
152		nop(skipEmptyCpp());
153		local sVersion;
154		if !readString(sVersion) error("version as a constant string expected for 'deprecated' on " + sMemberType + " '" + sMemberName + "'");
155		insert myMember.deprecated.name = sKeyword;
156		insert myMember.deprecated.version = sVersion;
157		nop(skipEmptyCpp());
158	}
159	if readIfEqualToIdentifier("bugs") {
160		nop(skipEmptyCpp());
161		local sBugs;
162		if !readString(sBugs) error("syntax error: known bugs of " + sMemberType + " '" + sMemberName + "' expected between quotes");
163		while skipEmptyCpp() && readString(sText) set sBugs += endl() + sText;
164		insert myMember.bugs = sBugs;
165		nop(skipEmptyCpp());
166	}
167	if readIfEqualToIdentifier("see") {
168		do {
169			nop(skipEmptyCpp());
170			local sKeyword = readIdentifier();
171			if !sKeyword error("keyword expected for 'see also' on " + sMemberType + " '" + sMemberName + "'");
172			nop(skipEmptyCpp());
173			insert myMember.seeAlso[sKeyword] = sKeyword;
174		} while readIfEqualTo(",");
175		nop(skipEmptyCpp());
176	}
177}
178
179insert project.headings["interpreter"].description = "Function for running a \\CodeWorker\\ script";
180insert project.headings["string"].description = "Functions for handling strings";
181insert project.headings["array"].description = "Functions handling arrays";
182insert project.headings["node"].description = "Functions handling a node";
183insert project.headings["iterator"].description = "Functions handling an iterator";
184insert project.headings["file"].description = "Functions handling files";
185insert project.headings["directory"].description = "Functions handling directories";
186insert project.headings["URL"].description = "Functions working on URL transfers (HTTP,...)";
187insert project.headings["datetime"].description = "Functions handling date-time";
188insert project.headings["numeric"].description = "Functions handling numbers";
189insert project.headings["standard"].description = "Classical functions of any standard library";
190insert project.headings["conversion"].description = "Type conversion";
191insert project.headings["system"].description = "Functions relative to the operating system";
192
193insert project.headings["command"].description = "Relative to the command line";
194insert project.headings["generation"].description = "Functions relative to generation";
195insert project.headings["parsing"].description = "Functions relative to scanning/parsing";
196insert project.headings["socket"].description = "Socket operations";
197insert project.headings["unknown"].description = "Various types of function";
198
199while skipEmptyCpp() {
200	if readIfEqualTo("#") {
201		local sLine;
202		if !readLine(sLine) break;
203		continue;
204	}
205	local sIdentifier = readIdentifier();
206	if !sIdentifier error("syntax error: 'procedure' or 'function' or 'method' keyword expected, instead of '" + readChar() + "'");
207	if sIdentifier == "procedure" {
208		nop(skipEmptyCpp());
209		local sMode;
210		if readIfEqualTo("[") {
211			nop(skipEmptyCpp());
212			set sMode = readIdentifier();
213			if !sMode error("syntax error: script mode expected, instead of '" + readChar() + "'");
214			if (sMode != "generate") && (sMode != "parse") error("syntax error: 'generate' or 'parse' keyword expected, instead of identifier '" + sIdentifier + "'");
215			nop(skipEmptyCpp());
216			if !readIfEqualTo("]") error("syntax error: ']' expected after script mode '" + sMode + "'");
217			nop(skipEmptyCpp());
218		}
219		local sFunction = readIdentifier();
220		if !sFunction error("syntax error: procedure name expected, instead of '" + readChar() + "'");
221		insert project.procedureList[sFunction].name = sFunction;
222		if sMode insert project.procedureList[sFunction].mode = sMode;
223		nop(skipEmptyCpp());
224		if !readIfEqualTo("(") error("syntax error: '(' expected after 'procedure " + sFunction + "'");
225		nop(skipEmptyCpp());
226		if !readIfEqualTo(")") {
227			local bHasDefault;
228			do {
229				nop(skipEmptyCpp());
230				local sParameter = readIdentifier();
231				insert project.procedureList[sFunction].parameterList[sParameter].name = sParameter;
232				nop(skipEmptyCpp());
233				if !readIfEqualTo(":") error("syntax error: ':' expected to declare type of parameter " + sParameter + "'");
234				nop(skipEmptyCpp());
235				parseType(project.procedureList[sFunction].parameterList[sParameter].type);
236				nop(skipEmptyCpp());
237				local sDefaultValue;
238				if readIfEqualTo(":") {
239					nop(skipEmptyCpp());
240					sDefaultValue = readIdentifier();
241					if sDefaultValue {
242						if sDefaultValue == "true" sDefaultValue = "\"true\"";
243						else if sDefaultValue == "false" sDefaultValue = "\"\"";
244						else if !(sDefaultValue in {"project", "this", "null"}) {
245							error("unknown constant variable '" + sDefaultValue + "'");
246						}
247					} else {
248						if readString(sDefaultValue) sDefaultValue = '"' + sDefaultValue.composeCLikeString() + '"';
249						else if !readNumber(sDefaultValue) {
250							error("default value expected");
251						}
252					}
253					insert project.procedureList[sFunction].parameterList[sParameter].default = sDefaultValue;
254					nop(skipEmptyCpp());
255					bHasDefault = true;
256				} else if bHasDefault {
257					error("default value expected after argument " + sParameter);
258				}
259				nop(skipEmptyCpp());
260				if !readIfEqualTo("=") error("syntax error: '=' expected to provide documentation of parameter '" + sParameter + "'");
261				nop(skipEmptyCpp());
262				local sDocumentation;
263				if !readString(sDocumentation) error("syntax error: documentation of parameter '" + sParameter + "' expected between quotes");
264				insert project.procedureList[sFunction].parameterList[sParameter].documentation = sDocumentation;
265				nop(skipEmptyCpp());
266			} while readIfEqualTo(",");
267			if !readIfEqualTo(")") error("syntax error: ')' expected to close parameter declaration of procedure '" + sFunction + "'");
268		}
269		nop(skipEmptyCpp());
270		if readIfEqualTo("[") {
271			nop(skipEmptyCpp());
272			local sModifier = readIdentifier();
273			if !sModifier error("syntax error: modifier expected for procedure '" + sFunction + "'");
274			if (sModifier != "user") && (sModifier != "info") && (sModifier != "visibility") error("syntax error: unrecognized modifier '" + sModifier + "' found for procedure '" + sFunction + "'");
275			insert project.procedureList[sFunction].modifierList[sModifier] = sModifier;
276			nop(skipEmptyCpp());
277			if !readIfEqualTo("]") error("syntax error: ']' expected at the end of modifier '" + sModifier + "'");
278			nop(skipEmptyCpp());
279		}
280		parseMemberDocumentation(project.procedureList[sFunction], sFunction, "procedure");
281		if !readIfEqualTo(";") error("syntax error: ';' expected at the end of procedure '" + sFunction + "'");
282	} else if sIdentifier == "function" {
283		nop(skipEmptyCpp());
284		local sMode;
285		if readIfEqualTo("[") {
286			nop(skipEmptyCpp());
287			set sMode = readIdentifier();
288			if !sMode error("syntax error: script mode expected, instead of '" + readChar() + "'");
289			if (sMode != "generate") && (sMode != "parse") error("syntax error: 'generate' or 'parse' keyword expected, instead of identifier '" + sIdentifier + "'");
290			nop(skipEmptyCpp());
291			if !readIfEqualTo("]") error("syntax error: ']' expected after script mode '" + sMode + "'");
292			nop(skipEmptyCpp());
293		}
294		local sFunction = readIdentifier();
295		if !sFunction error("syntax error: function name expected, instead of '" + readChar() + "'");
296		insert project.functionList[sFunction].name = sFunction;
297		if sMode insert project.functionList[sFunction].mode = sMode;
298		nop(skipEmptyCpp());
299		if !readIfEqualTo("(") error("syntax error: '(' expected after 'function " + sFunction + "'");
300		nop(skipEmptyCpp());
301		if !readIfEqualTo(")") {
302			local bHasDefault;
303			do {
304				nop(skipEmptyCpp());
305				local sParameter = readIdentifier();
306				insert project.functionList[sFunction].parameterList[sParameter].name = sParameter;
307				nop(skipEmptyCpp());
308				if !readIfEqualTo(":") error("syntax error: ':' expected to declare type of parameter " + sParameter + "'");
309				nop(skipEmptyCpp());
310				parseType(project.functionList[sFunction].parameterList[sParameter].type);
311				nop(skipEmptyCpp());
312				local sDefaultValue;
313				if readIfEqualTo(":") {
314					nop(skipEmptyCpp());
315					sDefaultValue = readIdentifier();
316					if sDefaultValue {
317						if sDefaultValue == "true" sDefaultValue = "\"true\"";
318						else if sDefaultValue == "false" sDefaultValue = "\"\"";
319						else if !(sDefaultValue in {"project", "this", "null"}) {
320							error("unknown constant variable '" + sDefaultValue + "'");
321						}
322					} else {
323						if !readNumber(sDefaultValue) {
324							if !readString(sDefaultValue) error("default value expected");
325							sDefaultValue = '"' + sDefaultValue.composeCLikeString() + '"';
326						}
327					}
328					insert project.functionList[sFunction].parameterList[sParameter].default = sDefaultValue;
329					nop(skipEmptyCpp());
330					bHasDefault = true;
331				} else if bHasDefault {
332					error("default value expected after argument " + sParameter);
333				}
334				nop(skipEmptyCpp());
335				if !readIfEqualTo("=") error("syntax error: '=' expected to provide documentation of parameter '" + sParameter + "'");
336				nop(skipEmptyCpp());
337				local sDocumentation;
338				if !readString(sDocumentation) error("syntax error: documentation of parameter '" + sParameter + "' expected between quotes");
339				insert project.functionList[sFunction].parameterList[sParameter].documentation = sDocumentation;
340				nop(skipEmptyCpp());
341			} while readIfEqualTo(",");
342			if !readIfEqualTo(")") error("syntax error: ')' expected to close parameter declaration of function '" + sFunction + "'");
343		}
344		nop(skipEmptyCpp());
345		if !readIfEqualTo(":") error("syntax error: ':' expected before declaring return type of function '" + sFunction + "'");
346		nop(skipEmptyCpp());
347		parseType(project.functionList[sFunction].return_type);
348		nop(skipEmptyCpp());
349		if readIfEqualTo("[") {
350			nop(skipEmptyCpp());
351			local sModifier = readIdentifier();
352			if !sModifier error("syntax error: modifier expected for function '" + sFunction + "'");
353			if (sModifier != "user") && (sModifier != "info") error("syntax error: unrecognized modifier '" + sModifier + "' found for function '" + sFunction + "'");
354			insert project.functionList[sFunction].modifierList[sModifier] = sModifier;
355			nop(skipEmptyCpp());
356			if !readIfEqualTo("]") error("syntax error: ']' expected at the end of modifier '" + sModifier + "'");
357			nop(skipEmptyCpp());
358		}
359		parseMemberDocumentation(project.functionList[sFunction], sFunction, "function");
360		if !readIfEqualTo(";") error("syntax error: ';' expected at the end of function '" + sFunction + "'");
361	} else if sIdentifier == "method" {
362		nop(skipEmptyCpp());
363		local sMethod = readIdentifier();
364		if !sMethod error("syntax error: method identifier expected after keyword 'method'");
365		nop(skipEmptyCpp());
366		if !readIfEqualToIdentifier("is") error("syntax error: 'is' expected after the declaration of method '" + sMethod +"'");
367		nop(skipEmptyCpp());
368		local sFunction = readIdentifier();
369		if !sFunction error("syntax error: function identifier expected after declaration of method '" + sMethod + "'");
370		nop(skipEmptyCpp());
371		if !findElement(sFunction, project.functionList) error("unknown function '" + sFunction + "' while declaring method '" + sMethod + "'");
372		insert project.methodList[sMethod].function = sFunction;
373		if readIfEqualTo("(") {
374			nop(skipEmptyCpp());
375			local sThis = readIdentifier();
376			if !sThis error("syntax error: 'this' parameter expected after declaration of method '" + sMethod + "'");
377			if !findElement(sThis, project.functionList[sFunction].parameterList) error("syntax error: '" + sThis + "' isn't a parameter on function '" + sFunction + "', while declaring method '" + sMethod + "'");
378			insert project.methodList[sMethod].thisParameter = sThis;
379			nop(skipEmptyCpp());
380			if !readIfEqualTo(")") error("syntax error: ')' expected after giving the 'this' parameter of method '" + sMethod + "'");
381			nop(skipEmptyCpp());
382		}
383		if !readIfEqualTo(";") error("syntax error: ';' expected at the end of method '" + sMethod + "'");
384		insert project.functionList[sFunction].method = sMethod;
385	} else {
386		error("syntax error: 'procedure' or 'function' or 'method' keyword expected, instead of identifier '" + sIdentifier + "'");
387	}
388}
389
390foreach i in project.functionList {
391	foreach j in i.seeAlso {
392		local myMember;
393		if findElement(j, project.functionList) {
394			ref myMember = project.functionList[j];
395		} else if findElement(j, project.procedureList) {
396			ref myMember = project.procedureList[j];
397		} else {
398			error("function '" + i.name + "' asks for seeing also function/procedure '" + j + "' that doesn't exist");
399		}
400		insert myMember.seeAlso[i.name] = i.name;
401		foreach k in i.seeAlso if k != j insert myMember.seeAlso[k] = k;
402	}
403}
404foreach i in project.procedureList {
405	foreach j in i.seeAlso {
406		local myMember;
407		if findElement(j, project.functionList) {
408			ref myMember = project.functionList[j];
409		} else if findElement(j, project.procedureList) {
410			ref myMember = project.procedureList[j];
411		} else {
412			error("procedure '" + i.name + "' asks for seeing also function/procedure '" + j + "' that doesn't exist");
413		}
414		insert myMember.seeAlso[i.name] = i.name;
415		foreach k in i.seeAlso if k != j insert myMember.seeAlso[k] = k;
416	}
417}
418