1 /*
2 	Copyright (C) 2008, 2009 Andres Cabrera
3 	mantaraya36@gmail.com
4 
5 	This file is part of CsoundQt.
6 
7 	CsoundQt is free software; you can redistribute it
8 	and/or modify it under the terms of the GNU Lesser General Public
9 	License as published by the Free Software Foundation; either
10 	version 2.1 of the License, or (at your option) any later version.
11 
12 	CsoundQt is distributed in the hope that it will be useful,
13 	but WITHOUT ANY WARRANTY; without even the implied warranty of
14 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 	GNU Lesser General Public License for more details.
16 
17 	You should have received a copy of the GNU Lesser General Public
18 	License along with Csound; if not, write to the Free Software
19 	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 	02111-1307 USA
21 */
22 
23 #include "opentryparser.h"
24 #include "types.h"
25 
26 #include <QFile>
27 
28 
OpEntryParser(QString opcodeFile)29 OpEntryParser::OpEntryParser(QString opcodeFile)
30 	: m_opcodeFile(opcodeFile)
31 {
32     m_udosMap = nullptr;
33 	QDomDocument m_doc("opcodes");
34 	QFile file(m_opcodeFile);
35 	if (!file.open(QIODevice::ReadOnly)) {
36 		qDebug() << "OpEntryParser::OpEntryParser could not find opcode file:" << opcodeFile;
37 		return;
38 	}
39 	if (!m_doc.setContent(&file)) {
40 		qDebug() << "OpEntryParser::OpEntryParser set content";
41 		file.close();
42 		return;
43 	}
44 	file.close();
45 	excludedOpcodes << "|" << "||" << "^" << "+" << "*" << "-" << "/";
46 	//      << "instr" << "endin" << "opcode" << "endop"
47 	//      << "sr" << "kr" << "ksmps" << "nchnls" << "0dbfs";
48 	QDomElement docElem = m_doc.documentElement();
49 	QList<Opcode> opcodesInCategoryList;
50 
51 	QDomElement cat = docElem.firstChildElement("category");
52 	while(!cat.isNull()) {
53 		QString catName = cat.attribute("name", "Miscellaneous");
54 		opcodesInCategoryList.clear();
55 		QDomElement opcode = cat.firstChildElement("opcode");
56 		while(!opcode.isNull()) {
57 			QDomElement desc = opcode.firstChildElement("desc");
58 			QString description = desc.text();
59 			QDomElement synop = opcode.firstChildElement("synopsis");
60 			while(!synop.isNull()) {
61 				Opcode op;
62 				op.desc = description;
63 				QDomElement s = synop.toElement();
64 				QDomNode node = s.firstChild();
65 				QDomElement elem = node.toElement();
66 				if (elem.tagName()=="opcodename") {
67 					op.opcodeName = elem.text().simplified();
68 					op.inArgs = node.nextSibling().toText().data();
69 				}
70 				else {
71 					op.outArgs = node.toText().data().simplified();
72 					node = node.nextSibling();
73 					op.opcodeName = node.toElement().text().simplified();
74 					node = node.nextSibling();
75 					if (!node.isNull())
76 						op.inArgs = node.toText().data().simplified();
77 				}
78 				if (op.opcodeName != "" && excludedOpcodes.count(op.opcodeName) == 0
79 						&& catName !="Utilities") {
80 					addOpcode(op);
81 					opcodesInCategoryList << op;
82 				}
83 				synop = synop.nextSiblingElement("synopsis");
84 			}
85 			opcode = opcode.nextSiblingElement("opcode");
86 		}
87 		QPair<QString, QList<Opcode> > newCategory(catName, opcodesInCategoryList);
88 		opcodeListCategory.append(opcodesInCategoryList);
89 		categoryList.append(catName);
90 		// 	qDebug() << "Category: " << categoryList.last();
91 		opcodeCategoryList.append(newCategory);
92 		cat = cat.nextSiblingElement("category");
93 	}
94 	addExtraOpcodes();
95 }
96 
~OpEntryParser()97 OpEntryParser::~OpEntryParser()
98 {
99 }
100 
addExtraOpcodes()101 void OpEntryParser::addExtraOpcodes()
102 {
103     Opcode opcode;
104 	opcode.outArgs = "";
105 	opcode.opcodeName = "then";
106 	opcode.inArgs ="";
107 	addOpcode(opcode);
108 
109     addFlag("use-system-sr", "Use the samplerate defined by the realtime audio backend");
110     addFlag("omacro", "--omacro:XXX=YYY set orchestra macro XXX to value YYY");
111     addFlag("port", "--port=N. listen to UDP port N for instruments/orchestra code");
112 
113 }
114 
opcodeNameList()115 QStringList OpEntryParser::opcodeNameList()
116 {
117 	QStringList list;
118 	//   qDebug("OpEntryParser::opcodeNameList() opcodeList.size() = %i",opcodeList.size());
119 	for (int i = 0; i<opcodeList.size();i++)  {
120 		list.append(opcodeList[i].opcodeName);
121 	}
122 	return list;
123 }
124 
addOpcode(Opcode opcode)125 void OpEntryParser::addOpcode(Opcode opcode)
126 {
127 	int i = 0;
128 	int size = opcodeList.size();
129 	while (i<size && opcodeList[i].opcodeName < opcode.opcodeName)
130 		i++;
131 	opcodeList.insert(i, opcode);
132 }
133 
addFlag(QString flag,QString desc)134 void OpEntryParser::addFlag(QString flag, QString desc) {
135     Opcode opcode;
136     opcode.desc = desc;
137     opcode.opcodeName = flag;
138     opcode.inArgs = "";
139     opcode.outArgs = "";
140     opcode.isFlag = 1;
141     opcodeList.append(opcode);
142 }
143 
findOpcode(QString opcodeName)144 Opcode OpEntryParser::findOpcode(QString opcodeName) {
145     for (int i = 0; i < opcodeList.size(); i++) {
146         if (opcodeList[i].opcodeName == opcodeName) {
147             return opcodeList[i];
148         }
149     }
150     if(m_udosMap->contains(opcodeName)) {
151         return m_udosMap->value(opcodeName);
152     }
153     qDebug() << "OpEntryParser::findOpcode: opcode " << opcodeName << "not found";
154     return Opcode();
155 }
156 
opcodeSyntax(Opcode opc)157 QString opcodeSyntax(Opcode opc) {
158     QString syntax = opc.outArgs.simplified();
159     if (!opc.outArgs.isEmpty())
160         syntax += " ";
161     syntax += opc.opcodeName.simplified();
162     if (!opc.inArgs.isEmpty()) {
163         syntax += " " + opc.inArgs.simplified();
164     }
165     if(!opc.desc.isEmpty()) {
166         syntax += "<br />[ " + opc.desc + " ]";
167     }
168     return syntax;
169 }
170 
getSyntax(QString opcodeName)171 QString OpEntryParser::getSyntax(QString opcodeName)
172 {
173     if (opcodeName.size() < 2) {
174         return "";
175 	}
176     Opcode opc = findOpcode(opcodeName);
177     if(!opc.opcodeName.isEmpty()) {
178         QString syntax = opcodeSyntax(opc);
179         return syntax;
180     }
181     return "";
182 }
183 
getPossibleSyntax(QString word)184 QVector<Opcode> OpEntryParser::getPossibleSyntax(QString word)
185 {
186     QVector<Opcode> out;
187 	for (int i = 0; i < opcodeList.size(); i++) {
188 		if (opcodeList[i].opcodeName.startsWith(word)) {
189 			out << opcodeList[i];
190 		}
191 	}
192     auto it = m_udosMap->constBegin();
193     while(it != m_udosMap->constEnd()) {
194         if(it->opcodeName.startsWith(word))
195             out << it.value();
196         it++;
197     }
198     return out;
199 }
200 
getOpcodesByCategory()201 QList< QPair<QString, QList<Opcode> > > OpEntryParser::getOpcodesByCategory()
202 {
203 	return opcodeCategoryList;
204 }
205 
getCategoryCount()206 int OpEntryParser::getCategoryCount()
207 {
208 	// qDebug("OpEntryParser::getCategoryCount()");
209 	return categoryList.size();
210 }
211 
getCategory(int index)212 QString OpEntryParser::getCategory(int index)
213 {
214 	if (index < categoryList.size())
215 		return categoryList[index];
216 	else return QString();
217 }
218 
getCategoryList()219 QStringList OpEntryParser::getCategoryList()
220 {
221 	return QStringList(categoryList);
222 }
223 
getOpcodeList(int index)224 QList<Opcode> OpEntryParser::getOpcodeList(int index)
225 {
226 	return opcodeListCategory[index];
227 }
228 
isOpcode(QString opcodeName)229 bool OpEntryParser::isOpcode(QString opcodeName)
230 {
231     for (int i = 0; i< opcodeList.size();i++)  {
232 		if (opcodeName == opcodeList[i].opcodeName) {
233             return true;
234         }
235 	}
236     if(this->m_udosMap->contains(opcodeName))
237         return true;
238     return false;
239 }
240 
241 
getOpcodeArgNames(Node & node)242 bool OpEntryParser::getOpcodeArgNames(Node &node)
243 {
244     QString opcodeName = node.getName();
245 	QVector<Port> inputs = node.getInputs();
246 	QVector<Port> outputs = node.getOutputs();
247     int idx = -1;
248     for (int i = 0; i< opcodeList.size();i++)  {
249         if (opcodeName == opcodeList[i].opcodeName) {
250             idx = i;
251             break;
252         }
253     }
254     Opcode opcode;
255     if(idx >= 0) {
256         opcode = opcodeList[idx];
257     } else {
258         if(m_udosMap->contains(opcodeName))
259             opcode = m_udosMap->value(opcodeName);
260         else
261             return false;
262     }
263     QString inArgs = opcode.inArgs;
264     // QString inArgs = opcodeList[idx].inArgs;
265     QString inArgsOpt = "";
266     if (inArgs.contains("["))
267         inArgsOpt = inArgs.mid(inArgs.indexOf("["));
268     inArgs.remove(inArgsOpt);
269     QStringList args = inArgs.split(QRegExp("[,\\\"]+"), QString::SkipEmptyParts);
270     for (int count = 0; count < args.size(); count ++) {
271         args[count] = args[count].trimmed();
272         if (args[count] == "")
273             args.removeAt(count);
274     }
275     QStringList argsOpt = inArgsOpt.split(QRegExp("[,\\\\\\s\\[\\]]+"), QString::SkipEmptyParts);
276     for (int j = 0; j < inputs.size(); j++) {
277         if (j < args.size()) {
278             inputs[j].argName = args[j];
279             inputs[j].optional = false;
280         }
281         else { //optional parameter
282             int index = j - args.size();
283             if (index < inArgsOpt.size()) {
284                 if (index < argsOpt.size()) {
285                     // in case the opcode contains an undefined number of optional arguments
286                     inputs[j].argName = argsOpt[index];
287                 }
288                 else {
289                     inputs[j].argName = "";
290                 }
291                 inputs[j].optional = true;
292                 //             qDebug() << "OpEntryParser::getOpcodeArgNames " <<  inputs[j].argName ;
293             }
294             else {
295                 qDebug("OpEntryParser::getOpcodeArgNames: Error too many inargs");
296             }
297         }
298     }
299     // QString outArgs = opcodeList[i].outArgs;
300     QString outArgs = opcode.outArgs;
301     QString outArgsOpt = "";
302     if (outArgs.contains("["))
303         outArgsOpt = outArgs.mid(outArgs.indexOf("["));
304     outArgs.remove(outArgsOpt);
305     args = outArgs.split(QRegExp("[,\\s]+"), QString::SkipEmptyParts);
306     argsOpt = outArgsOpt.split(QRegExp("[,\\\\\\s\\[\\]]"), QString::SkipEmptyParts);
307     for (int j = 0; j < outputs.size(); j++) {
308         if (j < args.size()) {
309             outputs[j].argName = args[j];
310             outputs[j].optional = false;
311         }
312         else { //optional parameter
313             int index = j - args.size();
314             if (index < inArgsOpt.size()) {
315                 if (index < argsOpt.size()) {
316                     // in case the opcode contains an undefined number of optional arguments
317                     outputs[j].argName = argsOpt[index];
318                 }
319                 else {
320                     outputs[j].argName = "";
321                 }
322                 outputs[j].optional = true;
323             }
324             else {
325                 qDebug("OpEntryParser::getOpcodeArgNames: Error too many outargs");
326             }
327         }
328     }
329     node.setInputs(inputs);
330     node.setOutputs(outputs);
331     return true;
332 }
333