001: /*
002: * $Id: Grammar.java,v 1.7 2002/09/16 08:05:03 jkl Exp $
003: *
004: * Copyright (c) 2002 Njet Communications Ltd. All Rights Reserved.
005: *
006: * Use is subject to license terms, as defined in
007: * Anvil Sofware License, Version 1.1. See LICENSE
008: * file, or http://njet.org/license-1.1.txt
009: */
010: package anvil.script;
011:
012: import java.util.ArrayList;
013: import java.util.StringTokenizer;
014: import anvil.core.Any;
015: import anvil.ErrorListener;
016: import anvil.Location;
017: import anvil.script.statements.Statement;
018: import anvil.script.statements.FunctionStatement;
019: import anvil.script.expression.ConstantNode;
020: import anvil.script.expression.Expression;
021: import anvil.script.expression.ExpressionList;
022: import anvil.script.expression.Node;
023: import anvil.script.expression.StringBufferNode;
024: import anvil.script.parser.ExpressionParser;
025: import anvil.script.parser.NestedParser;
026: import anvil.script.parser.ParserBase;
027: import anvil.script.parser.TemplateParser;
028: import anvil.util.Conversions;
029:
030: /**
031: * class Grammar
032: *
033: * @author: Jani Lehtimäki
034: */
035: public class Grammar {
036:
037: public static final boolean isValidIdentifier(String name) {
038: return isValidIdentifier(name, false);
039: }
040:
041: public static final boolean isValidIdentifier(String name,
042: boolean allowDot) {
043: if (name == null) {
044: return false;
045: }
046:
047: int n = name.length();
048: if (n == 0) {
049: return false;
050: }
051:
052: char ch = name.charAt(0);
053: if (ch == '$' || !Character.isJavaIdentifierStart(ch)) {
054: return false;
055: }
056:
057: for (int i = 1; i < n; i++) {
058: ch = name.charAt(i);
059: if (ch == '$' || !Character.isJavaIdentifierPart(ch)) {
060: if (!allowDot || ch != '.') {
061: return false;
062: }
063: }
064: }
065:
066: return true;
067: }
068:
069: public static final Expression parseExpression(String expression,
070: Location location, TemplateParser parser) {
071: return doParseExpression(ExpressionParser.TYPE_VALUE,
072: expression, location, parser);
073: }
074:
075: public static final Expression parseAssignmentExpression(
076: String expression, Location location, TemplateParser parser) {
077: return doParseExpression(ExpressionParser.TYPE_ASSIGNMENT,
078: expression, location, parser);
079: }
080:
081: public static final Expression parseStandaloneExpression(
082: String expression, Location location, TemplateParser parser) {
083: return doParseExpression(ExpressionParser.TYPE_STANDALONE,
084: expression, location, parser);
085: }
086:
087: public static final Expression parseAssignableExpression(
088: String expression, Location location, TemplateParser parser) {
089: return doParseExpression(ExpressionParser.TYPE_ASSIGNABLE,
090: expression, location, parser);
091: }
092:
093: public static final Expression[] parseForeachExpression(
094: String expression, Location location, TemplateParser parser) {
095: if (expression != null) {
096: ExpressionParser expressionParser = new ExpressionParser(
097: parser, location, expression);
098: return expressionParser.parseForeachExpression();
099: } else {
100: parser.error(location, "Expression attribute missing");
101: return new Expression[0];
102: }
103: }
104:
105: public static final Expression doParseExpression(int type,
106: String expression, Location location, TemplateParser parser) {
107: if (expression != null) {
108: ExpressionParser expressionParser = new ExpressionParser(
109: parser, location, expression);
110: return expressionParser.parseExpression(type);
111: } else {
112: parser.error(location, "Expression attribute missing");
113: return Expression.NULL;
114: }
115: }
116:
117: public static final Node parseString(ParserBase parser,
118: Location location, String image) {
119: if (image.indexOf("${") >= 0) {
120: ArrayList sections = new ArrayList();
121: StringBuffer buffer = new StringBuffer();
122: int n = image.length();
123: char ch;
124: for (int i = 0; i < n; i++) {
125: ch = image.charAt(i);
126: if ((i + 2 < n) && (ch == '$')
127: && (image.charAt(i + 1) == '{')) {
128: if (buffer.length() > 0) {
129: sections.add(new ConstantNode(Conversions
130: .unescape(buffer.toString(), false)));
131: }
132: buffer.setLength(0);
133:
134: boolean singleQuote = false;
135: boolean doubleQuote = false;
136: int nesting = 1;
137: int startIndex = i + 2;
138:
139: i++;
140: while (i + 1 < n && nesting > 0) {
141: ch = image.charAt(++i);
142: switch (ch) {
143: case '{':
144: if (!singleQuote || !doubleQuote) {
145: nesting++;
146: }
147: buffer.append(ch);
148: break;
149: case '}':
150: if (!singleQuote || !doubleQuote) {
151: nesting--;
152: }
153: if (nesting > 0) {
154: buffer.append(ch);
155: }
156: break;
157: case '\\':
158: if (i + 1 < n) {
159: char ch2 = image.charAt(++i);
160: if (ch2 == '}' || ch2 == '{') {
161: buffer.append(ch2);
162: } else {
163: buffer.append(ch);
164: buffer.append(ch2);
165: }
166: }
167: break;
168: case '\'':
169: if (doubleQuote == false) {
170: singleQuote = !singleQuote;
171: }
172: buffer.append(ch);
173: break;
174: case '"':
175: if (singleQuote == false) {
176: doubleQuote = !doubleQuote;
177: }
178: buffer.append(ch);
179: break;
180:
181: default:
182: buffer.append(ch);
183: }
184: }
185:
186: NestedParser expressionParser = new NestedParser(
187: parser, location, buffer.toString(),
188: startIndex);
189:
190: Expression expression = expressionParser
191: .parseExpression();
192:
193: if (expression.isConstant()) {
194: sections.add(new ConstantNode(expression.eval()
195: .toString()));
196: } else {
197: Node child = expression.getChild(0);
198: if (child != null) {
199: sections.add(child);
200: }
201: }
202: buffer.setLength(0);
203:
204: } else {
205: buffer.append(ch);
206: }
207: }
208:
209: if (buffer.length() > 0) {
210: sections.add(new ConstantNode(Conversions.unescape(
211: buffer.toString(), false)));
212: }
213:
214: n = sections.size();
215: ExpressionList list = new ExpressionList(n);
216: for (int i = 0; i < n; i++) {
217: list.setChild(i, (Node) sections.get(i));
218: }
219: return new StringBufferNode(list);
220:
221: } else {
222: return new ConstantNode(Conversions.unescape(image, false));
223: }
224:
225: }
226:
227: public static void checkInstanceAccess(ErrorListener listener,
228: Location location, Statement context, ClassType target) {
229: ClassType context_class = null;
230: if (context != null) {
231: context_class = context.getClassStatement();
232: }
233: while (target != null) {
234: ClassType[] required = target.getEnclosingClasses();
235: int n = required.length;
236: if (n > 0) {
237: if (context.isStaticRegion()) {
238: listener.error(location,
239: "Attempting to access instance of class '"
240: + target + "' from static region");
241: return;
242: }
243: }
244:
245: for (int i = 0; i < n; i++) {
246: boolean found = false;
247: ClassType parent = required[i];
248:
249: Type type = context_class;
250: while (type != null) {
251: if (type == parent) {
252: found = true;
253: break;
254: }
255: type = type.getParent();
256: }
257:
258: /*if (!found) {
259: ClassType clazz = context_class;
260: while(clazz != null) {
261: if (clazz == parent) {
262: found = true;
263: break;
264: }
265: clazz = clazz.getBaseClass();
266: }
267: }*/
268:
269: if (!found) {
270: listener.error(location, "Instance of enclosing '"
271: + parent + "' of '" + target
272: + "' is not accessible here");
273: }
274: }
275: target = target.getBaseClass();
276: }
277: }
278:
279: public static void checkInstanceAmbiguity(ErrorListener listener,
280: Location location, ClassType context, Type member) {
281: boolean accessible = false;
282: Type member_parent = member.getParent();
283: ClassType clazz = context;
284: while (clazz != null) {
285: if (clazz == member_parent) {
286: ClassType[] parents = context.getEnclosingClasses();
287: int n = parents.length;
288: for (int i = 0; i < n; i++) {
289: if (parents[i] == member_parent) {
290: listener
291: .error(
292: location,
293: "Ambiguous reference to '"
294: + member
295: + "' inherited from class '"
296: + member_parent
297: + "'. Explicit qualification is required.");
298: return;
299: }
300: }
301: return;
302: }
303: clazz = clazz.getBaseClass();
304: }
305: }
306:
307: public static final Name parseDottedName(String dottedname) {
308: return parseDottedName(null, null, dottedname);
309: }
310:
311: public static final Name parseDottedName(ErrorListener listener,
312: Location location, String dottedname) {
313: dottedname = dottedname.trim();
314: Name name = new Name();
315: boolean valid = true;
316: StringTokenizer tokenizer = new StringTokenizer(dottedname, ".");
317: while (tokenizer.hasMoreTokens()) {
318: String image = tokenizer.nextToken().trim();
319: if (!Grammar.isValidIdentifier(image)) {
320: if (listener != null) {
321: listener.error(location, "Syntax error, symbol '"
322: + image + "' is invalid");
323: }
324: valid = false;
325: }
326: name.add(image);
327: }
328: if (valid) {
329: return name;
330: } else {
331: return null;
332: }
333: }
334:
335: public static final Name[] parseDottedNames(ErrorListener listener,
336: Location location, String dottednames) {
337: boolean valid = true;
338: StringTokenizer tokenizer = new StringTokenizer(dottednames,
339: ",");
340: ArrayList list = new ArrayList();
341: while (tokenizer.hasMoreTokens()) {
342: Name name = parseDottedName(listener, location, tokenizer
343: .nextToken());
344: if (name != null) {
345: list.add(name);
346: } else {
347: valid = false;
348: }
349: }
350: if (valid) {
351: return (Name[]) list.toArray(new Name[list.size()]);
352: } else {
353: return null;
354: }
355:
356: }
357:
358: public static final Name[] parseImportNames(ErrorListener listener,
359: Location location, String names) {
360: boolean valid = true;
361: names = names.replace('\t', ' ');
362: names = names.replace('\r', ' ');
363: names = names.replace('\n', ' ');
364: StringTokenizer tokenizer = new StringTokenizer(names, ",");
365: ArrayList list = new ArrayList();
366: while (tokenizer.hasMoreTokens()) {
367: String token = tokenizer.nextToken() + ' ';
368: String as = null;
369: boolean star = false;
370: int i = token.indexOf(" as ");
371: if (i > 0) {
372: as = token.substring(i + 4).trim();
373: if (!isValidIdentifier(as)) {
374: listener.error(location, "Name '" + as
375: + "' is not a valid identifier");
376: }
377: token = token.substring(0, i).trim();
378: } else {
379: token = token.trim();
380: }
381: if (token.endsWith("*")) {
382: if (as != null) {
383: listener
384: .error(location,
385: "Syntax error: 'as' and '*' may not be used together in import");
386: }
387: star = true;
388: token = token.substring(0, token.length() - 2).trim();
389: if (token.endsWith(".")) {
390: token = token.substring(0, token.length() - 1);
391: }
392: }
393: Name name = parseDottedName(listener, location, token);
394: if (name != null) {
395: name.setAs(as);
396: if (star) {
397: name.enableStar();
398: }
399: list.add(name);
400: } else {
401: valid = false;
402: }
403: }
404: if (valid) {
405: return (Name[]) list.toArray(new Name[list.size()]);
406: } else {
407: return null;
408: }
409:
410: }
411:
412: public static final int countEscapeDepth(FunctionStatement context,
413: Statement target) {
414: int depth = 0;
415: Statement target_parent = target.getFunctionStatement()
416: .getContext();
417: while (context != null) {
418: if (context == target) {
419: break;
420: }
421: if (context == target_parent) {
422: depth--;
423: break;
424: }
425: depth++;
426: context = context.getContext();
427: }
428: if (context == null) {
429: return -1;
430: }
431: return depth;
432: }
433:
434: public static Type follow(Scope scope, String name) {
435: int start = 0;
436: int n = name.length();
437:
438: while (true) {
439: int end = name.indexOf('.', start + 1);
440: if (end == -1) {
441: end = name.length();
442: }
443:
444: Type type = scope.lookupDeclaration(name.substring(start,
445: end));
446: if (type == null) {
447: return null;
448: }
449:
450: if (end < n) {
451: if (type instanceof Scope) {
452: scope = (Scope) type;
453: } else {
454: return null;
455: }
456: } else {
457: return type;
458: }
459:
460: start = end + 1;
461: }
462: }
463:
464: public static final String buildQualifiedName(Type type) {
465: StringBuffer buffer = new StringBuffer(32);
466: if (buildQualifiedName0(buffer, (Scope) type.getParent())) {
467: buffer.append('.');
468: }
469: buffer.append(type.getName());
470: return buffer.toString();
471: }
472:
473: private static final boolean buildQualifiedName0(
474: StringBuffer buffer, Scope scope) {
475: if (scope == null) {
476: return false;
477: }
478: if (scope.getType() == Type.MODULE) {
479: return false;
480: }
481: if (buildQualifiedName0(buffer, scope.getParent())) {
482: buffer.append('.');
483: }
484: buffer.append(scope.getName());
485: return true;
486: }
487:
488: public static final Module getModuleOf(Type type) {
489: if (type.getType() == Type.MODULE) {
490: return (Module) type;
491: }
492: Scope scope = type.getParent();
493: while (scope != null) {
494: if (scope.getType() == Type.MODULE) {
495: return (Module) scope;
496: }
497: scope = scope.getParent();
498: }
499: return null;
500: }
501:
502: }
|