// Copyright (c) 2001, 2002, 2003, 2004, 2006 Per M.A. Bothner and Brainfood Inc.
// This is free software; for terms and warranty disclaimer see ./COPYING.
package gnu.xquery.lang;
import gnu.kawa.lispexpr.*;
import gnu.mapping.*;
import gnu.lists.*;
import gnu.text.*;
import gnu.expr.*;
import gnu.math.IntNum;
import java.util.Vector;
import java.util.Stack;
import java.io.File;
import gnu.kawa.xml.*;
import gnu.xml.*;
import gnu.bytecode.*;
import gnu.kawa.reflect.OccurrenceType;
import gnu.kawa.reflect.SingletonType;
import gnu.kawa.functions.Convert;
import gnu.xquery.util.NamedCollator;
import gnu.xquery.util.CastableAs;
import gnu.xquery.util.QNameUtils;
import gnu.xquery.util.RelativeStep;
import gnu.xquery.util.ValuesFilter;
import kawa.standard.require;
/** A class to read xquery forms. */
public class XQParser extends Lexer
{
int curToken;
Object curValue;
/** Normally null.
* 'C' means parsing the type of a 'cast as' or 'castable as'. */
int parseContext;
/** True if we've seen a VarDecl, FunctionDecl, or OptionDecl. */
boolean seenDeclaration;
String libraryModuleNamespace;
/** Value of getLineNumber() at start of current token.
* Sometimes set otherwise, to report errors. */
int curLine;
/** Value of getColumnNumber() at start of current token.
* Sometimes set otherwise, to report errors. */
int curColumn;
XQuery interpreter;
int seenPosition;
int seenLast;
public static boolean warnOldVersion = true;
public static boolean warnHidePreviousDeclaration = false;
/** The internal name of the variable containing '.', the context node. */
static final Symbol DOT_VARNAME = Symbol.makeUninterned("$dot$");
/** The pseduo-function position() is mapped to a reference. */
static final Symbol POSITION_VARNAME = Symbol.makeUninterned("$position$");
/** The pseduo-function last() is mapped to a reference to this variable. */
static final Symbol LAST_VARNAME = Symbol.makeUninterned("$last$");
public static final gnu.kawa.reflect.InstanceOf instanceOf
= new gnu.kawa.reflect.InstanceOf(XQuery.getInstance(), "instance");
public static final CastableAs castableAs = CastableAs.castableAs;
public static final Convert treatAs = Convert.as;
NameLookup lexical;
NamedCollator defaultCollator = null;
/** The default order for empty sequences.
* Either <code>'L'</code> (for "least") or <code>'G'</code> (for "greatest").
*/
char defaultEmptyOrder = 'L';
boolean emptyOrderDeclarationSeen;
Path baseURI = null;
boolean baseURIDeclarationSeen;
public void setStaticBaseUri (String uri)
{
try
{
baseURI = fixupStaticBaseUri(URIPath.valueOf(uri));
}
catch (Throwable ex)
{
if (ex instanceof WrappedException)
ex = ((WrappedException) ex).getCause();
error('e', "invalid URI: "+ex.getMessage());
}
}
static Path fixupStaticBaseUri (Path path)
{
path = path.getAbsolute();
if (path instanceof FilePath)
path = URIPath.valueOf(path.toURI());
return path;
}
public String getStaticBaseUri ()
{
Path path = baseURI;
if (path == null)
{
Environment env = Environment.getCurrent();
Object value = env.get(Symbol.make("", "base-uri"), null, null);
if (value != null)
{
if (value instanceof Path)
path = (Path) path;
else
path = URIPath.valueOf(value.toString());
}
if (path == null)
{
LineBufferedReader port = getPort();
path = port.getPath();
if (path instanceof FilePath
&& (! path.exists()
|| port instanceof TtyInPort
|| port instanceof CharArrayInPort))
path = null;
}
if (path == null)
path = Path.currentPath();
path = fixupStaticBaseUri(path);
baseURI = path;
}
return path.toString();
}
public String resolveAgainstBaseUri (String uri)
{
if (Path.uriSchemeSpecified(uri))
return uri;
String base = getStaticBaseUri();
Path basePath = Path.valueOf(base);
return basePath.resolve(uri).toString();
}
boolean boundarySpacePreserve;
boolean boundarySpaceDeclarationSeen;
boolean orderingModeUnordered;
boolean orderingModeSeen;
/** True if we've seen a 'copy-namespaces' declaration'. */
boolean copyNamespacesDeclarationSeen;
int copyNamespacesMode
= XMLFilter.COPY_NAMESPACES_PRESERVE|XMLFilter.COPY_NAMESPACES_INHERIT;
/** The static construction mode. True if "strip"; false if "preserve". */
boolean constructionModeStrip;
/** True if a construction mode declaration has been seen. */
boolean constructionModeDeclarationSeen;
public Namespace[] functionNamespacePath
= XQuery.defaultFunctionNamespacePath;
/** Stack of currently active for/let Declarations. */
Declaration[] flworDecls;
/* Index in flworDecls of first Declaration in current FLWOR. */
int flworDeclsFirst;
/* Total number of currently active for/let Declarations. */
int flworDeclsCount;
int parseCount;
int commentCount;
/** An error message if comments are disallowed. Normally null. */
String errorIfComment;
/** Skip whitespace.
* Sets 'index' to the that of the next non-whitespace character,
* and returns that. If there are no more non-space characters,
* returns ' '. */
final int skipSpace()
throws java.io.IOException, SyntaxException
{
return skipSpace(true);
}
final int skipSpace(boolean verticalToo)
throws java.io.IOException, SyntaxException
{
for (;;)
{
int ch = read();
if (ch == '(')
{
if (! checkNext(':'))
return '(';
skipComment();
}
else if (ch == '{')
{
ch = read();
if (ch != '-')
{
unread(ch);
return '{';
}
ch = read();
if (ch != '-')
{
unread(ch);
unread('-');
return '{';
}
skipOldComment();
}
else if (verticalToo
? (ch < 0 || ! Character.isWhitespace((char) ch))
: (ch != ' ' && ch != '\t'))
return ch;
}
}
final void skipToSemicolon ()
throws java.io.IOException
{
for (;;)
{
int next = read();
if (next < 0 || next == ';')
break;
}
}
final void skipOldComment()
throws java.io.IOException, SyntaxException
{
int seenDashes = 0;
int startLine = getLineNumber() + 1;
int startColumn = getColumnNumber() - 2;
warnOldVersion("use (: :) instead of old-style comment {-- --}");
for (;;)
{
int ch = read();
if (ch == '-')
seenDashes++;
else if (ch == '}' && seenDashes >= 2)
return;
else if (ch < 0)
{
curLine = startLine;
curColumn = startColumn;
eofError("non-terminated comment starting here");
}
else
seenDashes = 0;
}
}
final void skipComment()
throws java.io.IOException, SyntaxException
{
commentCount++;
int startLine = getLineNumber() + 1;
int startColumn = getColumnNumber() - 1;
if (errorIfComment != null)
{
curLine = startLine;
curColumn = startColumn;
error('e', errorIfComment);
}
int prev = 0;
int commentNesting = 0;
char saveReadState = pushNesting(':');
for (;;)
{
int ch = read();
if (ch == ':')
{
if (prev == '(')
{
commentNesting++;
ch = 0;
}
}
else if (ch == ')' && prev == ':')
{
if (commentNesting == 0)
{
popNesting(saveReadState);
return;
}
--commentNesting;
}
else if (ch < 0)
{
curLine = startLine;
curColumn = startColumn;
eofError("non-terminated comment starting here");
}
prev = ch;
}
}
/** Do skipSpace followed by unread to find next non-space character. */
final int peekNonSpace(String message)
throws java.io.IOException, SyntaxException
{
int ch = skipSpace();
if (ch < 0)
eofError(message);
unread(ch);
return ch;
}
static final int EOF_TOKEN = -1;
static final int EOL_TOKEN = '\n';
static final char INTEGER_TOKEN = '0';
static final char DECIMAL_TOKEN = '1';
static final char DOUBLE_TOKEN = '2';
static final int STRING_TOKEN = '"';
static final int SLASHSLASH_TOKEN = 'D';
static final int DOTDOT_TOKEN = '3';
static final int COLON_EQUAL_TOKEN = 'L'; // ":="
static final int COLON_COLON_TOKEN = 'X';
/** A non-qualified (simple) name (NCName).
* The tokenBuffer contains the name (which does not contain a ':'). */
static final int NCNAME_TOKEN = 'A';
/** A non-qualified (simple) name (NCName) followed by a colon.
* The colon is not followed by another NCNAME (in which it would
* be a QNAME_TOKEN instead).
* The tokenBuffer contains the name (which does not contain the ':'). */
static final int NCNAME_COLON_TOKEN = 'C';
/** A Qualified name (QName).
* The tokenBuffer contains the full name, which contains one ':'. */
static final int QNAME_TOKEN = 'Q';
static final int ARROW_TOKEN = 'R';
/* FuncName including following '('). */
static final int FNAME_TOKEN = 'F';
static final int IMPORT_MODULE_TOKEN = 'I'; // <"import" "module">
static final int IMPORT_SCHEMA_TOKEN = 'T'; // <"import" "schema">
static final int MODULE_NAMESPACE_TOKEN = 'M'; // <"module" "namespace">
static final int DECLARE_NAMESPACE_TOKEN = 'N'; // <"declare" "namespace">
static final int DECLARE_BOUNDARY_SPACE_TOKEN = 'S'; // <"declare" "boundary-space">
static final int DEFAULT_ELEMENT_TOKEN = 'E'; // <"declare" "default" "element">
static final int DEFAULT_FUNCTION_TOKEN = 'O'; // <"declare" "default" "function">
static final int DEFAULT_COLLATION_TOKEN = 'G';
static final int DEFAULT_ORDER_TOKEN = 'H'; // <"declare" "default" "order">
static final int DECLARE_FUNCTION_TOKEN = 'P'; // <"declare" "function">
static final int DECLARE_VARIABLE_TOKEN = 'V'; // <"declare" "variable">
static final int DECLARE_BASE_URI_TOKEN = 'B'; // <"declare" "base-uri">
static final int DECLARE_ORDERING_TOKEN = 'U'; // <"declare" "ordering">
static final int DECLARE_CONSTRUCTION_TOKEN = 'K'; // <"declare" "construction">
static final int DECLARE_OPTION_TOKEN = 'o'; // <"declare" "option">
static final int DECLARE_COPY_NAMESPACES_TOKEN = 'L'; // <"declare" "copy-namespaces">
static final int DEFINE_QNAME_TOKEN = 'W'; // <"define" QName> - an error
static final int XQUERY_VERSION_TOKEN = 'Y'; // <"xquery" "version">
/* 'Q': QName (intern'ed name is curValue)
* 'R': NCName ':' '*'
* OP_AXIS_FIRST: 'ancestor' followed by '::'
* ...
* OP_AXIS_FIRST+AXIS_SELF: 'self' followed by '::'
*/
static final int OP_AXIS_FIRST = 100;
static final int COUNT_OP_AXIS = 13;
static final int AXIS_ANCESTOR = 0;
static final int AXIS_ANCESTOR_OR_SELF = 1;
static final int AXIS_ATTRIBUTE = 2;
static final int AXIS_CHILD = 3;
static final int AXIS_DESCENDANT = 4;
static final int AXIS_DESCENDANT_OR_SELF = 5;
static final int AXIS_FOLLOWING = 6;
static final int AXIS_FOLLOWING_SIBLING = 7;
static final int AXIS_NAMESPACE = 8;
static final int AXIS_PARENT = 9;
static final int AXIS_PRECEDING = 10;
static final int AXIS_PRECEDING_SIBLING = 11;
static final int AXIS_SELF = 12;
static final int OP_WHERE = 196;
static final int PRAGMA_START_TOKEN = 197; // '{#'
// Token types for binary operators.
static final int OP_BASE = 400;
static final int OP_OR = OP_BASE; // 'or'
static final int OP_AND = OP_BASE + 1; // 'and'
static final int OP_EQU = OP_BASE + 2; // ' ='
static final int OP_NEQ = OP_BASE + 3; // '! ='
static final int OP_LSS = OP_BASE + 4; // '<'
static final int OP_GRT = OP_BASE + 5; // '>'
static final int OP_LEQ = OP_BASE + 6; // '< ='
static final int OP_GEQ = OP_BASE + 7; // '> ='
static final int OP_IS = OP_BASE + 8; // 'is'
static final int OP_ISNOT = OP_BASE + 9; // 'isnot'
static final int OP_GRTGRT = OP_BASE + 10; // '>>'
static final int OP_LSSLSS = OP_BASE + 11; // '<<'
static final int OP_RANGE_TO = OP_BASE + 12; // 'to'
static final int OP_ADD = OP_BASE + 13; // '+'
static final int OP_SUB = OP_BASE + 14; // '-'
static final int OP_MUL = OP_BASE + 15; // '*'
static final int OP_DIV = OP_BASE + 16; // 'div'
static final int OP_IDIV = OP_BASE + 17; // 'idiv'
static final int OP_MOD = OP_BASE + 18; // 'mod'
static final int OP_UNION = OP_BASE + 19; // 'union'
static final int OP_INTERSECT = OP_BASE + 20; // 'intersect'
static final int OP_EXCEPT = OP_BASE + 21; // 'except'
static final int OP_INSTANCEOF = OP_BASE + 22; // 'instance' 'of'
static final int OP_TREAT_AS = OP_BASE + 23; // 'treat' 'as'
static final int OP_CASTABLE_AS= OP_BASE + 24; // 'castable' 'as'
static final int OP_CAST_AS = OP_BASE + 25; // 'cast' 'as'
static final int OP_EQ = OP_BASE + 26; // 'eq'
static final int OP_NE = OP_BASE + 27; // 'ne'
static final int OP_LT = OP_BASE + 28; // 'lt'
static final int OP_LE = OP_BASE + 29; // 'le
static final int OP_GT = OP_BASE + 30; // 'gt'
static final int OP_GE = OP_BASE + 31; // 'ge'
static final int OP_NODE = 230; // 'node' followed by '('
static final int OP_TEXT = 231; // 'text' followed by '('
static final int OP_COMMENT = 232; // 'comment' followed by '('
static final int OP_PI = 233; // 'processing-instruction' '('
static final int OP_DOCUMENT = 234; // 'document-node' '('
static final int OP_ELEMENT = 235; // 'element' '('
static final int OP_ATTRIBUTE = 236; // 'attribute' '('
static final int OP_ITEM = 237; // 'item' '('
static final int OP_EMPTY_SEQUENCE = 238; // 'empty-sequence' '('
static final int OP_SCHEMA_ATTRIBUTE = 239; // 'schema-attribute' '('
static final int OP_SCHEMA_ELEMENT = 240; // 'schema-element' '('
static final int IF_LPAREN_TOKEN = 241; // 'if' '('
static final int TYPESWITCH_LPAREN_TOKEN = 242; // 'typeswitch' '('
static final int FOR_DOLLAR_TOKEN = 243; // 'for' '$'
static final int LET_DOLLAR_TOKEN = 244; // 'let' '$'
static final int SOME_DOLLAR_TOKEN = 245; // 'some' '$'
static final int EVERY_DOLLAR_TOKEN = 246; // 'every' '$'
static final int CASE_DOLLAR_TOKEN = 247; // 'case' '$'
static final int VALIDATE_LBRACE_TOKEN = 248; // 'validate' '{'
static final int ORDERED_LBRACE_TOKEN = 249; // 'ordered' '{'
static final int UNORDERED_LBRACE_TOKEN = 250; // 'unordered' '{'
static final int ELEMENT_TOKEN = 251; // 'element' followed by '{' or alpha
static final int ATTRIBUTE_TOKEN = 252;// 'attribute' followed by '{' or alpha
static final int TEXT_TOKEN = 253; // 'text' followed by '{'
static final int COMMENT_TOKEN = 254; // 'text' followed by '{'
static final int PI_TOKEN = 255; // 'processing-instruction' followed by '{' or alpha
static final int DOCUMENT_TOKEN = 256; // ;document' followed by '{'
private int saveToken;
private Object saveValue;
public void mark ()
throws java.io.IOException
{
super.mark();
saveToken = curToken;
saveValue = curValue;
}
public void reset()
throws java.io.IOException
{
curToken = saveToken;
curValue = saveValue;
super.reset();
}
private int setToken (int token, int width)
{
curToken = token;
curLine = port.getLineNumber() + 1;
curColumn = port.getColumnNumber() + 1 - width;
return token;
}
void checkSeparator (char ch)
{
if (XName.isNameStart(ch))
error('e', "missing separator", "XPST0003");
}
int getRawToken()
throws java.io.IOException, SyntaxException
{
int next;
for (;;)
{
next = read();
if (next < 0)
return setToken(EOF_TOKEN, 0);
if (next == '\n' || next == '\r')
{
if (nesting <= 0)
return setToken(EOL_TOKEN, 0);
}
else if (next == '(')
{
if (checkNext(':'))
skipComment();
else if (checkNext('#'))
return setToken(PRAGMA_START_TOKEN, 2);
else
return setToken('(', 1);
}
else if (next == '{')
{
if (! checkNext('-'))
return setToken('{', 1);
next = read();
if (next != '-')
{
// FIXME backup 2 chars. Can fix using special token for '{-'.
unread();
unread();
return setToken('{', 1);
}
skipOldComment();
}
else if (next != ' ' && next != '\t')
break;
}
tokenBufferLength = 0;
curLine = port.getLineNumber() + 1;
curColumn = port.getColumnNumber();
char ch = (char) next;
switch (ch)
{
case ')': case '[': case ']': case '}':
case '$': case '@': case ',': case '?': case ';':
break;
case ':':
if (checkNext('='))
ch = COLON_EQUAL_TOKEN;
else if (checkNext(':'))
ch = COLON_COLON_TOKEN;
break;
case '|':
ch = OP_UNION;
break;
case '*':
ch = OP_MUL;
break;
case '+':
ch = OP_ADD;
break;
case '-':
ch = OP_SUB;
break;
case '!':
if (checkNext('='))
ch = OP_NEQ;
break;
case '/':
if (checkNext('/'))
ch = SLASHSLASH_TOKEN;
break;
case '=':
if (checkNext('>'))
ch = ARROW_TOKEN;
ch = OP_EQU;
break;
case '>':
ch = checkNext('=') ? (char) OP_GEQ
: checkNext('>') ? (char) OP_GRTGRT : (char) OP_GRT;
break;
case '<':
ch = checkNext('=') ? (char) OP_LEQ
: checkNext('<') ? (char) OP_LSSLSS : (char) OP_LSS;
break;
case '\'': case '\"':
char saveReadState = pushNesting ((char) next);
for (;;)
{
next = read();
if (next < 0)
eofError("unexpected end-of-file in string starting here");
if (next == '&')
{
parseEntityOrCharRef();
continue;
}
else if (ch == next)
{
next = read ();
if (ch != next)
{
unread(next);
break;
}
}
tokenBufferAppend((char) next);
}
popNesting(saveReadState);
ch = STRING_TOKEN;
break;
default:
if (Character.isDigit(ch)
|| (ch == '.' && Character.isDigit((char) peek())))
{
boolean seenDot = ch == '.';
for (;; )
{
tokenBufferAppend(ch);
next = read();
if (next < 0)
break;
ch = (char) next;
if (ch == '.')
{
if (seenDot) break;
seenDot = true;
}
else if (! Character.isDigit(ch))
break;
}
if (next == 'e' || next == 'E')
{
tokenBufferAppend((char) next);
next = read();
if (next == '+' || next == '-')
{
tokenBufferAppend((char) next);
next = read();
}
int expDigits = 0;
for (;;)
{
if (next < 0)
break;
ch = (char) next;
if (! Character.isDigit(ch))
{
checkSeparator(ch);
unread();
break;
}
tokenBufferAppend(ch);
next = read();
expDigits++;
}
if (expDigits == 0)
error('e', "no digits following exponent", "XPST0003");
ch = DOUBLE_TOKEN;
}
else
{
ch = seenDot ? DECIMAL_TOKEN : INTEGER_TOKEN;
if (next >= 0)
{
checkSeparator((char) next);
unread(next);
}
}
}
else if (ch == '.')
{
if (checkNext('.'))
ch = DOTDOT_TOKEN;
break;
}
else if (XName.isNameStart(ch))
{
for (;;)
{
tokenBufferAppend(ch);
next = read();
ch = (char) next;
if (! XName.isNamePart(ch))
break;
}
if (next < 0)
ch = NCNAME_TOKEN;
else
{
if (next != ':')
ch = NCNAME_TOKEN;
else
{
next = read();
if (next < 0)
eofError("unexpected end-of-file after NAME ':'");
ch = (char) next;
if (XName.isNameStart(ch))
{
tokenBufferAppend(':');
for (;;)
{
tokenBufferAppend(ch);
next = read();
ch = (char) next;
if (! XName.isNamePart(ch))
break;
}
ch = QNAME_TOKEN;
}
else if (ch == '=')
{
unread(ch);
ch = NCNAME_TOKEN;
}
else
ch = NCNAME_COLON_TOKEN;
}
unread(next);
}
}
else if (ch >= ' ' && ch < 127)
syntaxError("invalid character '"+ch+'\'');
else
syntaxError("invalid character '\\u"+Integer.toHexString(ch)+'\'');
}
curToken = ch;
return ch;
}
/** Scan until a given delimiter.
* On success, text upto the delimiter is in then tokenBuffer (with
* tokenBufferLength marking its length); the delimiter is not included.
*/
public void getDelimited(String delimiter)
throws java.io.IOException, SyntaxException
{
tokenBufferLength = 0;
int dlen = delimiter.length();
char last = delimiter.charAt(dlen-1);
for (;;)
{
int ch = read();
if (ch < 0)
eofError("unexpected end-of-file looking for '"+delimiter+'\'');
int dstart, j;
// Look for a match for the last delimiter character.
if (ch == last
&& (dstart = tokenBufferLength - (j = dlen - 1)) >= 0)
{
// Check that the initial part of the delimiter has also been seen.
do
{
if (j == 0)
{
tokenBufferLength = dstart;
return;
}
j--;
}
while (tokenBuffer[dstart+j] == delimiter.charAt(j));
}
tokenBufferAppend((char) ch);
}
}
public void appendNamedEntity(String name)
{
name = name.intern();
char ch = '?';
if (name == "lt")
ch = '<';
else if (name == "gt")
ch = '>';
else if (name == "amp")
ch = '&';
else if (name == "quot")
ch = '"';
else if (name == "apos")
ch = '\'';
else
error("unknown enity reference: '"+name+"'");
tokenBufferAppend(ch);
}
boolean match (String word1, String word2, boolean force)
throws java.io.IOException, SyntaxException
{
if (match(word1))
{
mark();
getRawToken();
if (match(word2))
{
reset();
getRawToken();
return true;
}
reset();
if (force)
{
error('e', "'"+word1+"' must be followed by '"+word2+"'",
"XPST0003");
return true;
}
}
return false;
}
/** Return the current token, assuming it is in operator context.
* Resolve NCNAME_TOKEN (identifier) to 'and', 'or', 'div', etc.
*/
int peekOperator()
throws java.io.IOException, SyntaxException
{
while (curToken == EOL_TOKEN)
{
if (nesting == 0)
return EOL_TOKEN;
getRawToken();
}
if (curToken == NCNAME_TOKEN)
{
int len = tokenBufferLength;
char c1, c2, c3;
switch (len)
{
case 2:
c1 = tokenBuffer[0];
c2 = tokenBuffer[1];
if (c1 == 'o' && c2 == 'r')
curToken = OP_OR;
else if (c1 == 't' && c2 == 'o')
curToken = OP_RANGE_TO;
else if (c1 == 'i' && c2 == 's')
curToken = OP_IS;
else if (c1 == 'e' && c2 == 'q')
curToken = OP_EQ;
else if (c1 == 'n' && c2 == 'e')
curToken = OP_NE;
else if (c1 == 'g')
{
if (c2 == 'e') curToken = OP_GE;
else if (c2 == 't') curToken = OP_GT;
}
else if (c1 == 'l')
{
if (c2 == 'e') curToken = OP_LE;
else if (c2 == 't') curToken = OP_LT;
}
break;
case 3:
c1 = tokenBuffer[0];
c2 = tokenBuffer[1];
c3 = tokenBuffer[2];
if (c1 == 'a')
{
if (c2 == 'n' && c3 == 'd')
curToken = OP_AND;
}
else if (c1 == 'm') {
if (c2 == 'u' && c3 == 'l')
curToken = OP_MUL;
if (c2 == 'o' && c3 == 'd')
curToken = OP_MOD;
}
else if (c1 == 'd') {
if (c2 == 'i' && c3 == 'v')
curToken = OP_DIV;
}
break;
case 4:
if (match("idiv"))
curToken = OP_IDIV;
else if (match("cast", "as", true))
curToken = OP_CAST_AS;
break;
case 5:
if (match("where"))
curToken = OP_WHERE;
else if (match("isnot"))
curToken = OP_ISNOT;
else if (match("union"))
curToken = OP_UNION;
else if (match("treat", "as", true))
curToken = OP_TREAT_AS;
break;
case 6:
if (match("except"))
curToken = OP_EXCEPT;
break;
case 8:
if (match("instance", "of", true))
curToken = OP_INSTANCEOF;
else if (match("castable", "as", true))
curToken = OP_CASTABLE_AS;
break;
case 9:
if (match("intersect"))
curToken = OP_INTERSECT;
break;
case 10:
if (match("instanceof")) // obsolete
{
warnOldVersion("use 'instanceof of' (two words) instead of 'instanceof'");
curToken = OP_INSTANCEOF;
}
break;
default:
break;
}
}
return curToken;
}
/**
* Internal method to match against double-lexeme tokens.
* @param word0 expected previous word
* @param word1 expected next word
*/
private boolean lookingAt (String word0, String word1)
throws java.io.IOException, SyntaxException
{
if (! word0.equals(curValue))
return false;
int i = 0;
int len = word1.length();
for (;; )
{
int ch = read();
if (i == len)
{
if (ch < 0)
return true;
if ( ! XName.isNamePart((char) ch))
{
unread();
return true;
}
i++;
break;
}
if (ch < 0 || ch != word1.charAt(i++))
break;
}
port.skip(-i);
return false;
}
int getAxis ()
{
// match axis name
String name = new String(tokenBuffer, 0, tokenBufferLength).intern();
int i;
for (i = COUNT_OP_AXIS; --i >= 0; )
if (axisNames[i] == name)
break;
if (i < 0
|| i == AXIS_NAMESPACE) // The namespace-axis is XSLT/XPath-only.
{
error('e', "unknown axis name '" + name + '\'', "XPST0003");
i = AXIS_CHILD;
}
return (char) (OP_AXIS_FIRST + i);
}
/** Process token, assuming we are in operand context.
*/
int peekOperand()
throws java.io.IOException, SyntaxException
{
while (curToken == EOL_TOKEN)
getRawToken();
if (curToken == NCNAME_TOKEN || curToken == QNAME_TOKEN)
{
int next = skipSpace(nesting != 0);
switch (tokenBuffer[0])
{
case 'a':
if (match("attribute"))
{
if (next == '(')
return curToken = OP_ATTRIBUTE;
if (next == '{' || XName.isNameStart((char) next))
{
unread();
return curToken = ATTRIBUTE_TOKEN;
}
break;
}
break;
case 'c':
if (match("comment"))
{
if (next == '(')
return curToken = OP_COMMENT;
if (next == '{')
{
unread();
return curToken = COMMENT_TOKEN;
}
}
break;
case 'd':
if (next == '{' && match("document"))
{
unread();
return curToken = DOCUMENT_TOKEN;
}
if (next == '(' && match("document-node"))
return curToken = OP_DOCUMENT;
break;
case 'e':
if (match("element"))
{
if (next == '(')
return curToken = OP_ELEMENT;
if (next == '{' || XName.isNameStart((char) next))
{
unread();
return curToken = ELEMENT_TOKEN;
}
break;
}
if (match("empty-sequence"))
return curToken = OP_EMPTY_SEQUENCE;
if (next == '$' && match("every"))
return curToken = EVERY_DOLLAR_TOKEN;
break;
case 'f':
if (next == '$' && match("for"))
return curToken = FOR_DOLLAR_TOKEN;
break;
case 'i':
if (next == '(' && match("if"))
return curToken = IF_LPAREN_TOKEN;
if (next == '(' && match("item"))
return curToken = OP_ITEM;
break;
case 'l':
if (next == '$' && match("let"))
return curToken = LET_DOLLAR_TOKEN;
break;
case 'n':
if (next == '(' && match("node"))
return curToken = OP_NODE;
break;
case 'o':
if (next == '{' && match("ordered"))
return curToken = ORDERED_LBRACE_TOKEN;
break;
case 'p':
if (match("processing-instruction"))
{
if (next == '(')
return curToken = OP_PI;
if (next == '{' || XName.isNameStart((char) next))
{
unread();
return curToken = PI_TOKEN;
}
break;
}
break;
case 's':
if (next == '$' && match("some"))
return curToken = SOME_DOLLAR_TOKEN;
if (next == '(' && match("schema-attribute"))
return curToken = OP_SCHEMA_ATTRIBUTE;
if (next == '(' && match("schema-element"))
return curToken = OP_SCHEMA_ELEMENT;
break;
case 't':
if (match("text"))
{
if (next == '(')
return curToken = OP_TEXT;
if (next == '{')
{
unread();
return curToken = TEXT_TOKEN;
}
}
if (next == '(' && match("typeswitch"))
return curToken = TYPESWITCH_LPAREN_TOKEN;
break;
case 'u':
if (next == '{' && match("unordered"))
return curToken = UNORDERED_LBRACE_TOKEN;
break;
case 'v':
if (next == '{' && match("validate"))
return curToken = VALIDATE_LBRACE_TOKEN;
break;
}
if (next == '(' && peek() != ':')
{
return curToken = FNAME_TOKEN;
}
if (next == ':' && peek() == ':')
return curToken = getAxis();
String name = new String(tokenBuffer, 0, tokenBufferLength);
curValue = name;
switch (next)
{
case 'b':
if (lookingAt("declare", /*"b"+*/ "ase-uri"))
return curToken = DECLARE_BASE_URI_TOKEN;
if (lookingAt("declare", /*"b"+*/ "oundary-space"))
return curToken = DECLARE_BOUNDARY_SPACE_TOKEN;
break;
case 'c':
if (lookingAt("declare", /*"c"+*/ "onstruction"))
return curToken = DECLARE_CONSTRUCTION_TOKEN;
if (lookingAt("declare", /*"c"+*/ "opy-namespaces"))
return curToken = DECLARE_COPY_NAMESPACES_TOKEN;
break;
case 'd':
if (lookingAt("declare", /*"d"+*/ "efault"))
{
getRawToken();
if (match("function"))
return curToken = DEFAULT_FUNCTION_TOKEN;
if (match("element"))
return curToken = DEFAULT_ELEMENT_TOKEN;
if (match("collation"))
return curToken = DEFAULT_COLLATION_TOKEN;
if (match("order"))
return curToken = DEFAULT_ORDER_TOKEN;
error("unrecognized/unimplemented 'declare default'");
skipToSemicolon();
return peekOperand();
}
case 'e':
if (lookingAt("default", /*"e"+*/ "lement"))
{
warnOldVersion("replace 'default element' by 'declare default element namespace'");
return curToken = DEFAULT_ELEMENT_TOKEN;
}
break;
case 'f':
if (lookingAt("declare", /*"f"+*/ "unction"))
return curToken = DECLARE_FUNCTION_TOKEN;
if (lookingAt("define", /*"f"+*/ "unction"))
{
warnOldVersion("replace 'define function' by 'declare function'");
return curToken = DECLARE_FUNCTION_TOKEN;
}
if (lookingAt("default", /*"f"+*/ "unction"))
{
warnOldVersion("replace 'default function' by 'declare default function namespace'");
return curToken = DEFAULT_FUNCTION_TOKEN;
}
break;
case 'm':
if (lookingAt("import", /*"m"+*/ "odule"))
return curToken = IMPORT_MODULE_TOKEN;
break;
case 'n':
if (lookingAt("declare", /*"n"+*/ "amespace"))
return curToken = DECLARE_NAMESPACE_TOKEN;
if (lookingAt("default", /*"n"+*/ "amespace"))
{
warnOldVersion("replace 'default namespace' by 'declare default element namespace'");
return curToken = DEFAULT_ELEMENT_TOKEN;
}
if (lookingAt("module", /*"n"+*/ "amespace"))
return curToken = MODULE_NAMESPACE_TOKEN;
break;
case 'o':
if (lookingAt("declare", /*"o"+*/ "rdering"))
return curToken = DECLARE_ORDERING_TOKEN;
if (lookingAt("declare", /*"o"+*/ "ption"))
return curToken = DECLARE_OPTION_TOKEN;
break;
case 's':
if (lookingAt("import", /*"s"+*/ "chema"))
return curToken = IMPORT_SCHEMA_TOKEN;
break;
case 'v':
if (lookingAt("declare", /*"v"+*/ "ariable"))
return curToken = DECLARE_VARIABLE_TOKEN;
if (lookingAt("define", /*"v"+*/ "ariable"))
{
warnOldVersion("replace 'define variable' by 'declare variable'");
return curToken = DECLARE_VARIABLE_TOKEN;
}
if (lookingAt("xquery", /*"v"+*/ "ersion"))
return curToken = XQUERY_VERSION_TOKEN;
break;
case 'x':
if (lookingAt("declare", /*"x"+*/ "mlspace"))
{
warnOldVersion("replace 'define xmlspace' by 'declare boundary-space'");
return curToken = DECLARE_BOUNDARY_SPACE_TOKEN;
}
break;
}
if (next >= 0)
{
unread();
if (XName.isNameStart((char) next) && curValue.equals("define"))
{
getRawToken();
curToken = DEFINE_QNAME_TOKEN;
}
}
return curToken;
}
if (curToken == NCNAME_COLON_TOKEN)
{
int next = read();
if (next == ':') // We've seen an Axis specifier.
curToken = getAxis();
else
unread(next);
}
return curToken;
}
void checkAllowedNamespaceDeclaration (String prefix, String uri,
boolean inConstructor)
{
boolean xmlPrefix = "xml".equals(prefix);
if (NamespaceBinding.XML_NAMESPACE.equals(uri))
{
if (! xmlPrefix || ! inConstructor)
error('e', "namespace uri cannot be the same as the prefined xml namespace",
"XQST0070");
}
else if (xmlPrefix || "xmlns".equals(prefix))
error('e', "namespace prefix cannot be 'xml' or 'xmlns'",
"XQST0070");
}
void pushNamespace(String prefix, String uri)
{
if (uri.length() == 0)
uri = null;
prologNamespaces = new NamespaceBinding(prefix, uri, prologNamespaces);
}
public XQParser(InPort port, SourceMessages messages, XQuery interp)
{
super(port, messages);
interpreter = interp;
lexical = new NameLookup(interp);
nesting = 1;
// Push standard namespaces into lexical scope.
NamespaceBinding ns = builtinNamespaces;
prologNamespaces = ns;
}
public void setInteractive(boolean v)
{
if (interactive != v)
if (v) nesting--; else nesting++;
interactive = v;
}
private static final int priority(int opcode)
{
switch (opcode)
{
case OP_OR:
return 1;
case OP_AND:
return 2;
case OP_EQU: case OP_NEQ:
case OP_LSS: case OP_GRT: case OP_LEQ: case OP_GEQ:
case OP_EQ: case OP_NE:
case OP_LT: case OP_GT: case OP_LE: case OP_GE:
case OP_IS: case OP_ISNOT:
case OP_GRTGRT: case OP_LSSLSS:
return 3;
case OP_RANGE_TO:
return 4;
case OP_ADD: case OP_SUB:
return 5;
case OP_MUL: case OP_DIV: case OP_IDIV: case OP_MOD:
return 6;
case OP_UNION:
return 7;
case OP_INTERSECT: case OP_EXCEPT:
return 8;
case OP_INSTANCEOF:
return 9;
case OP_TREAT_AS:
return 10;
case OP_CASTABLE_AS:
return 11;
case OP_CAST_AS:
return 12;
default:
return 0;
}
}
static Expression makeBinary(Expression func,
Expression exp1, Expression exp2)
{
Expression[] args = new Expression[2];
args[0] = exp1;
args[1] = exp2;
return new ApplyExp(func, args);
}
static Expression makeExprSequence(Expression exp1, Expression exp2)
{
return makeBinary(makeFunctionExp
("gnu.kawa.functions.AppendValues", "appendValues"),
exp1, exp2);
}
Expression makeBinary(int op, Expression exp1, Expression exp2)
throws java.io.IOException, SyntaxException
{
Expression func;
switch (op)
{
case OP_ADD:
func = makeFunctionExp("gnu.xquery.util.ArithOp", "add", "+");
break;
case OP_SUB:
func = makeFunctionExp("gnu.xquery.util.ArithOp", "sub", "-");
break;
case OP_MUL:
func = makeFunctionExp("gnu.xquery.util.ArithOp", "mul", "*");
break;
case OP_DIV:
func = makeFunctionExp("gnu.xquery.util.ArithOp", "div", "div");
break;
case OP_IDIV:
func = makeFunctionExp("gnu.xquery.util.ArithOp", "idiv", "idiv");
break;
case OP_MOD:
func = makeFunctionExp("gnu.xquery.util.ArithOp", "mod", "mod");
break;
case OP_EQ:
func = makeFunctionExp("gnu.xquery.util.Compare", "valEq", "eq");
break;
case OP_NE:
func = makeFunctionExp("gnu.xquery.util.Compare", "valNe", "ne");
break;
case OP_LT:
func = makeFunctionExp("gnu.xquery.util.Compare", "valLt", "lt");
break;
case OP_LE:
func = makeFunctionExp("gnu.xquery.util.Compare", "valLe", "le");
break;
case OP_GT:
func = makeFunctionExp("gnu.xquery.util.Compare", "valGt", "gt");
break;
case OP_GE:
func = makeFunctionExp("gnu.xquery.util.Compare", "valGe", "ge");
break;
case OP_EQU:
func = makeFunctionExp("gnu.xquery.util.Compare", "=");
break;
case OP_NEQ:
func = makeFunctionExp("gnu.xquery.util.Compare", "!=");
break;
case OP_LSS:
func = makeFunctionExp("gnu.xquery.util.Compare", "<");
break;
case OP_LEQ:
func = makeFunctionExp("gnu.xquery.util.Compare", "<=");
break;
case OP_GRT:
func = makeFunctionExp("gnu.xquery.util.Compare", ">");
break;
case OP_GEQ:
func = makeFunctionExp("gnu.xquery.util.Compare", ">=");
break;
case OP_IS:
func = makeFunctionExp("gnu.kawa.xml.NodeCompare", "$Eq", "is");
break;
case OP_ISNOT:
func = makeFunctionExp("gnu.kawa.xml.NodeCompare", "$Ne", "isnot");
break;
case OP_GRTGRT:
func = makeFunctionExp("gnu.kawa.xml.NodeCompare", "$Gr", ">>");
break;
case OP_LSSLSS:
func = makeFunctionExp("gnu.kawa.xml.NodeCompare", "$Ls", "<<");
break;
case OP_RANGE_TO:
func = makeFunctionExp("gnu.xquery.util.IntegerRange", "integerRange");
break;
case OP_UNION:
func = makeFunctionExp("gnu.kawa.xml.UnionNodes", "unionNodes");
break;
case OP_INTERSECT:
func = makeFunctionExp("gnu.kawa.xml.IntersectNodes",
"intersectNodes");
break;
case OP_EXCEPT:
func = makeFunctionExp("gnu.kawa.xml.IntersectNodes", "exceptNodes");
break;
default:
return syntaxError("unimplemented binary op: "+op);
}
return makeBinary(func, exp1, exp2);
}
private void parseSimpleKindType ()
throws java.io.IOException, SyntaxException
{
getRawToken();
if (curToken == ')')
getRawToken();
else
error("expected ')'");
}
public Expression parseNamedNodeType (boolean attribute)
throws java.io.IOException, SyntaxException
{
Expression qname;
getRawToken();
if (curToken == ')')
{
qname = QuoteExp.getInstance(ElementType.MATCH_ANY_QNAME);
getRawToken();
}
else
{
if (curToken == QNAME_TOKEN || curToken == NCNAME_TOKEN)
qname = parseNameTest(attribute);
else
{
if (curToken != OP_MUL)
syntaxError("expected QName or *");
qname = QuoteExp.getInstance(ElementType.MATCH_ANY_QNAME);
}
getRawToken();
if (curToken == ',')
{
getRawToken();
if (curToken == QNAME_TOKEN || curToken == NCNAME_TOKEN)
{
Expression tname = parseNameTest(true);
}
else
syntaxError("expected QName");
getRawToken();
}
if (curToken == ')')
getRawToken();
else
error("expected ')' after element");
}
return makeNamedNodeType(attribute, qname);
}
static Expression makeNamedNodeType (boolean attribute, Expression qname)
{
Expression[] name = new Expression[2];
ClassType nodeType = ClassType.make(attribute
? "gnu.kawa.xml.AttributeType"
: "gnu.kawa.xml.ElementType");
ApplyExp elt = new ApplyExp(nodeType.getDeclaredMethod("make", 1),
new Expression[] { qname });
elt.setFlag(ApplyExp.INLINE_IF_CONSTANT);
return elt;
}
private boolean warnedOldStyleKindTest;
private void warnOldStyleKindTest()
{
if (warnedOldStyleKindTest)
return;
error('w', "old-style KindTest - first one here");
warnedOldStyleKindTest = true;
}
/** Parse: ["as" SequenceType] */
public Expression parseOptionalTypeDeclaration ()
throws java.io.IOException, SyntaxException
{
if (! match("as"))
return null;
getRawToken();
return parseDataType();
}
public Expression parseDataType()
throws java.io.IOException, SyntaxException
{
Expression etype = parseItemType();
int min, max;
if (etype == null)
{
if (curToken != OP_EMPTY_SEQUENCE)
return syntaxError("bad syntax - expected DataType");
parseSimpleKindType();
if (curToken == '?' || curToken == OP_ADD || curToken == OP_MUL)
{
getRawToken();
return syntaxError("occurrence-indicator meaningless after empty-sequence()");
}
etype = QuoteExp.getInstance(OccurrenceType.emptySequenceType);
min = 0;
max = 0;
}
else if (curToken == '?')
{
min = 0;
max = 1;
}
else if (curToken == OP_ADD)
{
min = 1;
max = -1;
}
else if (curToken == OP_MUL)
{
min = 0;
max = -1;
}
else
{
min = 1;
max = 1;
}
if (parseContext == 'C')
{
if (max != 1)
return syntaxError("type to 'cast as' or 'castable as' must be a 'SingleType'");
}
if (min != max)
{
getRawToken();
Expression[] args = { etype,
QuoteExp.getInstance(gnu.math.IntNum.make(min)),
QuoteExp.getInstance(gnu.math.IntNum.make(max)) };
ApplyExp otype
= new ApplyExp(ClassType.make("gnu.kawa.reflect.OccurrenceType")
.getDeclaredMethod("getInstance", 3),
args);
otype.setFlag(ApplyExp.INLINE_IF_CONSTANT);
return otype;
}
return etype;
}
public Expression parseMaybeKindTest ()
throws java.io.IOException, SyntaxException
{
Type type;
switch (curToken)
{
case OP_ATTRIBUTE:
case OP_ELEMENT:
return parseNamedNodeType(curToken == OP_ATTRIBUTE);
case OP_TEXT:
parseSimpleKindType();
type = NodeType.textNodeTest;
break;
case OP_COMMENT:
parseSimpleKindType();
type = NodeType.commentNodeTest;
break;
case OP_DOCUMENT:
parseSimpleKindType();
type = NodeType.documentNodeTest;
break;
case OP_NODE:
parseSimpleKindType();
type = NodeType.anyNodeTest;
break;
case OP_PI:
getRawToken();
String piTarget = null;
if (curToken == NCNAME_TOKEN || curToken == STRING_TOKEN)
{
piTarget = new String(tokenBuffer, 0, tokenBufferLength);
getRawToken();
}
if (curToken == ')')
getRawToken();
else
error("expected ')'");
type = ProcessingInstructionType.getInstance(piTarget);
break;
default:
return null;
}
return QuoteExp.getInstance(type);
}
public Expression parseItemType()
throws java.io.IOException, SyntaxException
{
peekOperand();
Expression etype = parseMaybeKindTest();
Type type;
if (etype != null)
{
if (parseContext == 'C')
// Kludge to force error below.
type = XDataType.anyAtomicType;
else
return etype;
}
else if (curToken == OP_ITEM)
{
parseSimpleKindType();
type = SingletonType.getInstance();
}
else if (curToken == NCNAME_TOKEN || curToken == QNAME_TOKEN)
{
String tname = new String(tokenBuffer, 0, tokenBufferLength);
getRawToken();
type = interpreter.getTypeFor(tname);
if (type == null)
{
error('e', "unknown type "+tname, "XPST0051");
type = Type.pointer_type;
}
}
else
return null;
if (parseContext == 'C')
{
if (type == SingletonType.getInstance())
return syntaxError("type to 'cast as' or 'castable as' must be atomic", "XPST0080");
if (type == XDataType.anyAtomicType)
return syntaxError("type to 'cast as' or 'castable as' cannot be anyAtomicType", "XPST0080");
if (type == XDataType.NotationType)
return syntaxError("type to 'cast as' or 'castable as' cannot be NOTATION", "XPST0080");
}
return QuoteExp.getInstance(type);
}
/** Parse a <code>URILiteral</code>..
* @return either a String (on success),
* or an ErrorExp (after emitting an error).
*/
Object parseURILiteral ()
throws java.io.IOException, SyntaxException
{
getRawToken();
if (curToken != STRING_TOKEN)
return declError("expected a URILiteral");
String str = new String(tokenBuffer, 0, tokenBufferLength);
str = TextUtils.replaceWhitespace(str, true);
// FUTURE: An implementation MAY raise a static error if the value
// of a URILiteral is of nonzero length and is not in the lexical
// space of xs:anyURI, or if it is a string that represents a
// relative URI as defined in [RFC2396]. err:XQST0046
return str;
}
Expression parseExpr()
throws java.io.IOException, SyntaxException
{
return parseExprSingle();
}
final Expression parseExprSingle ()
throws java.io.IOException, SyntaxException
{
int startLine = curLine;
int startColumn = curColumn;
peekOperand();
switch (curToken)
{
// FIXME old code tweaked line/column
// as in:
// maybeSetLine(exp, startLine, startColumn - 3);
case IF_LPAREN_TOKEN:
return parseIfExpr();
case TYPESWITCH_LPAREN_TOKEN:
return parseTypeSwitch();
case FOR_DOLLAR_TOKEN:
return parseFLWRExpression(true);
case LET_DOLLAR_TOKEN:
return parseFLWRExpression(false);
case SOME_DOLLAR_TOKEN:
return parseQuantifiedExpr(false);
case EVERY_DOLLAR_TOKEN:
return parseQuantifiedExpr(true);
default:
return parseBinaryExpr(priority(OP_OR));
}
}
Expression parseBinaryExpr(int prio)
throws java.io.IOException, SyntaxException
{
Expression exp = parseUnaryExpr();
for (;;)
{
int token = peekOperator();
if (token == EOL_TOKEN
// Following makes for better error handling.
|| (token == OP_LSS && peek() == '/'))
return exp;
int tokPriority = priority(token);
if (tokPriority < prio)
return exp;
char saveReadState = pushNesting('%');
getRawToken();
popNesting(saveReadState);
if (token >= OP_INSTANCEOF && token <= OP_CAST_AS)
{
if (token == OP_CAST_AS || token == OP_CASTABLE_AS)
parseContext = 'C';
Expression type = parseDataType();
parseContext = '\0';
Expression[] args = new Expression[2];
Expression func;
switch (token)
{
case OP_INSTANCEOF:
args[0] = exp;
args[1] = type;
func = makeFunctionExp("gnu.xquery.lang.XQParser",
"instanceOf");
break;
case OP_CASTABLE_AS:
args[0] = exp;
args[1] = type;
func = new ReferenceExp(XQResolveNames.castableAsDecl);
break;
case OP_TREAT_AS:
args[0] = type;
args[1] = exp;
func = makeFunctionExp("gnu.xquery.lang.XQParser",
"treatAs");
break;
default: // i.e. case OP_CAST_AS:
args[0] = type;
args[1] = exp;
func = new ReferenceExp(XQResolveNames.castAsDecl);
break;
}
exp = new ApplyExp(func, args);
}
else if (token == OP_INSTANCEOF)
{
Expression[] args = { exp, parseDataType() };
exp = new ApplyExp(makeFunctionExp("gnu.xquery.lang.XQParser",
"instanceOf"),
args);
}
else
{
Expression exp2 = parseBinaryExpr(tokPriority+1);
if (token == OP_AND)
exp = new IfExp(booleanValue(exp), booleanValue(exp2), XQuery.falseExp);
else if (token == OP_OR)
exp = new IfExp(booleanValue(exp), XQuery.trueExp, booleanValue(exp2));
else
exp = makeBinary(token, exp, exp2);
}
}
}
Expression parseUnaryExpr()
throws java.io.IOException, SyntaxException
{
Expression exp;
if (curToken == OP_SUB || curToken == OP_ADD)
{
int op = curToken;
getRawToken();
exp = parseUnaryExpr();
Expression func
= makeFunctionExp("gnu.xquery.util.ArithOp",
op == OP_ADD ? "plus" : "minus",
op == OP_ADD ? "+" : "-");
exp = new ApplyExp(func, new Expression[] { exp });
}
else
exp = parseUnionExpr();
return exp;
}
Expression parseUnionExpr()
throws java.io.IOException, SyntaxException
{
Expression exp = parseIntersectExceptExpr();
for (;;)
{
int op = peekOperator();
if (op != OP_UNION)
break;
getRawToken();
Expression exp2 = parseIntersectExceptExpr();
exp = makeBinary(op, exp, exp2);
}
return exp;
}
Expression parseIntersectExceptExpr()
throws java.io.IOException, SyntaxException
{
Expression exp = parsePathExpr();
for (;;)
{
int op = peekOperator();
if (op != OP_INTERSECT && op != OP_EXCEPT)
break;
getRawToken();
Expression exp2 = parsePathExpr();
exp = makeBinary(op, exp, exp2);
}
return exp;
}
Expression parsePathExpr()
throws java.io.IOException, SyntaxException
{
Expression step1;
if (curToken == '/' || curToken == SLASHSLASH_TOKEN)
{
Declaration dotDecl = lexical.lookup(DOT_VARNAME, false);
Expression dot;
if (dotDecl == null)
dot = syntaxError("context item is undefined", "XPDY0002");
else
dot = new ReferenceExp(DOT_VARNAME, dotDecl);
step1 = new ApplyExp(ClassType.make("gnu.xquery.util.NodeUtils")
.getDeclaredMethod("rootDocument", 1),
new Expression[] { dot } );
int next = skipSpace(nesting != 0);
unread(next);
if (next < 0 || next == ')' || next == '}')
{
getRawToken();
return step1;
}
}
else
step1 = parseStepExpr();
return parseRelativePathExpr(step1);
}
/** Returns an expression that evaluates to a Symbol.
* The expression will normally be constant
* folded to a Symbol, but we cannot do that yet. */
Expression parseNameTest (boolean attribute)
throws java.io.IOException, SyntaxException
{
String local = null, prefix = null;
if (curToken == QNAME_TOKEN)
{
int colon = tokenBufferLength;
while (tokenBuffer[--colon] != ':') ;
prefix = new String(tokenBuffer, 0, colon);
colon++;
local = new String(tokenBuffer, colon,
tokenBufferLength - colon);
}
else if (curToken == OP_MUL)
{
int next = read();
local = ElementType.MATCH_ANY_LOCALNAME;
if (next != ':')
unread(next);
else
{
next = read();
if (next < 0)
eofError("unexpected end-of-file after '*:'");
if (XName.isNameStart((char) next))
{
unread();
getRawToken();
if (curToken != NCNAME_TOKEN)
syntaxError("invalid name test");
else
local = new String(tokenBuffer, 0, tokenBufferLength)
.intern();
}
else if (next != '*')
syntaxError("missing local-name after '*:'");
}
return QuoteExp.getInstance(new Symbol(null, local));
}
else if (curToken == NCNAME_TOKEN)
{
local = new String(tokenBuffer, 0, tokenBufferLength);
if (attribute)
return new QuoteExp(Namespace.EmptyNamespace.getSymbol(local.intern()));
prefix = null;
}
else if (curToken == NCNAME_COLON_TOKEN)
{
prefix = new String(tokenBuffer, 0, tokenBufferLength);
int next = read();
if (next != '*')
syntaxError("invalid characters after 'NCName:'");
local = ElementType.MATCH_ANY_LOCALNAME;
}
if (prefix != null)
prefix = prefix.intern();
Expression[] args = new Expression[3];
args[0] = new ApplyExp(new ReferenceExp(XQResolveNames.resolvePrefixDecl),
new Expression[] { QuoteExp.getInstance(prefix) });
args[1] = new QuoteExp(local == null ? "" : local);
args[2] = new QuoteExp(prefix);
ApplyExp make = new ApplyExp(Compilation.typeSymbol
.getDeclaredMethod("make", 3),
args);
make.setFlag(ApplyExp.INLINE_IF_CONSTANT);
return make;
}
Expression parseNodeTest(int axis)
throws java.io.IOException, SyntaxException
{
int token = peekOperand();
Expression[] args = new Expression[1];
Expression etype = parseMaybeKindTest();
if (etype != null)
{
args[0] = etype;
}
else if (curToken == NCNAME_TOKEN || curToken == QNAME_TOKEN
|| curToken == NCNAME_COLON_TOKEN || curToken == OP_MUL)
{
args[0] = makeNamedNodeType(axis == AXIS_ATTRIBUTE,
parseNameTest(axis == AXIS_ATTRIBUTE) );
}
else if (axis >= 0)
return syntaxError("unsupported axis '"+axisNames[axis]+"::'");
else
return null;
Declaration dotDecl = lexical.lookup(DOT_VARNAME, false);
Expression dot;
if (dotDecl == null)
dot = syntaxError("node test when context item is undefined", "XPDY0002");
else
dot = new ReferenceExp(DOT_VARNAME, dotDecl);
if (etype == null)
getRawToken();
Expression makeAxisStep;
if (axis == AXIS_CHILD || axis == -1)
makeAxisStep = makeChildAxisStep;
else if (axis == AXIS_DESCENDANT)
makeAxisStep = makeDescendantAxisStep;
else
{
String axisName;
switch (axis)
{
case AXIS_DESCENDANT_OR_SELF: axisName = "DescendantOrSelf"; break;
case AXIS_SELF: axisName = "Self"; break;
case AXIS_PARENT: axisName = "Parent"; break;
case AXIS_ANCESTOR: axisName = "Ancestor"; break;
case AXIS_ANCESTOR_OR_SELF: axisName = "AncestorOrSelf"; break;
case AXIS_FOLLOWING: axisName = "Following"; break;
case AXIS_FOLLOWING_SIBLING: axisName = "FollowingSibling"; break;
case AXIS_PRECEDING: axisName = "Preceding"; break;
case AXIS_PRECEDING_SIBLING: axisName = "PrecedingSibling"; break;
case AXIS_ATTRIBUTE: axisName = "Attribute"; break;
default: throw new Error();
}
makeAxisStep
= QuoteExp.getInstance(new PrimProcedure
("gnu.kawa.xml."+axisName+"Axis",
"make", 1));
}
ApplyExp mkAxis = new ApplyExp(makeAxisStep, args);
mkAxis.setFlag(ApplyExp.INLINE_IF_CONSTANT);
return new ApplyExp(mkAxis, new Expression[] { dot });
}
public static QuoteExp makeChildAxisStep
= QuoteExp.getInstance(new PrimProcedure("gnu.kawa.xml.ChildAxis", "make", 1));
public static QuoteExp makeDescendantAxisStep
= QuoteExp.getInstance(new PrimProcedure("gnu.kawa.xml.DescendantAxis", "make", 1));
Expression parseRelativePathExpr(Expression exp)
throws java.io.IOException, SyntaxException
{
// If the previous operator was '//', then the corresponding E1.
Expression beforeSlashSlash = null;
while (curToken == '/' || curToken == SLASHSLASH_TOKEN)
{
boolean descendants = curToken == SLASHSLASH_TOKEN;
LambdaExp lexp = new LambdaExp(3);
Declaration dotDecl = lexp.addDeclaration(DOT_VARNAME);
dotDecl.setFlag(Declaration.IS_SINGLE_VALUE);
dotDecl.setType(NodeType.anyNodeTest);
dotDecl.noteValue (null); // Does not have a known value.
lexp.addDeclaration(POSITION_VARNAME, LangPrimType.intType);
lexp.addDeclaration(LAST_VARNAME, LangPrimType.intType);
comp.push(lexp);
if (descendants)
{
curToken = '/';
Expression dot = new ReferenceExp(DOT_VARNAME, dotDecl);
Expression[] args = { dot };
TreeScanner op = DescendantOrSelfAxis.anyNode;
lexp.body = new ApplyExp(op, args);
beforeSlashSlash = exp;
}
else
{
getRawToken();
Expression exp2 = parseStepExpr();
// Optimize: 'E1//child::TEST' to 'E1/descendant::TEST'
Expression func;
ApplyExp aexp;
if (beforeSlashSlash != null
&& exp2 instanceof ApplyExp
&& (func = ((ApplyExp) exp2).getFunction()) instanceof ApplyExp
&& (aexp = (ApplyExp) func).getFunction() == makeChildAxisStep)
{
aexp.setFunction(makeDescendantAxisStep);
exp = beforeSlashSlash;
}
lexp.body = exp2;
beforeSlashSlash = null;
}
comp.pop(lexp);
/*
if (lexp.body instanceof ApplyExp)
{
// Optimize the case of a simple name step.
ApplyExp aexp = (ApplyExp) lexp.body;
Expression func = aexp.getFunction();
Expression[] args = aexp.getArgs();
if (false
&& func == funcNamedChildren && args.length==2
&& args[0] instanceof ReferenceExp
&& ((ReferenceExp) args[0]).getBinding() == decl)
{
args[0] = exp;
if (descendants)
func = funcNamedDescendants;
exp = new ApplyExp (func, args);
handled = true;
}
else if (func == funcForwardFilter && args.length==2
&& args[0] instanceof ApplyExp
&& descendants)
{
ApplyExp xapp = (ApplyExp) args[0];
Expression[] xargs = xapp.getArgs();
if (xapp.getFunction() == funcNamedChildren
&& xargs.length == 2
&& ((ReferenceExp) xargs[0]).getBinding() == decl)
{
xapp.setFunction(funcNamedDescendants);
}
}
}
*/
Expression[] args = new Expression[] { exp, lexp };
exp = new ApplyExp(RelativeStep.relativeStep, args);
}
return exp;
}
Expression parseStepExpr()
throws java.io.IOException, SyntaxException
{
int axis;
if (curToken == '.' || curToken == DOTDOT_TOKEN)
{
axis = curToken == '.' ? AXIS_SELF : AXIS_PARENT;
getRawToken();
Declaration dotDecl = lexical.lookup(DOT_VARNAME, false);
Expression exp;
if (dotDecl == null)
exp = syntaxError("context item is undefined", "XPDY0002");
else
exp = new ReferenceExp(DOT_VARNAME, dotDecl);
if (axis == AXIS_PARENT)
{
Expression[] args = { exp };
exp = new ApplyExp(ParentAxis.make(NodeType.anyNodeTest), args);
}
// Note that '..' is an AbbrevReverseStep,
// but '.' is a FilterExpr - and hence not a valid ForwardStep.
return parseStepQualifiers(exp, axis == AXIS_SELF ? -1 : axis);
}
axis = peekOperand() - OP_AXIS_FIRST;
Expression unqualifiedStep;
if (axis >= 0 && axis < COUNT_OP_AXIS)
{
getRawToken();
unqualifiedStep = parseNodeTest(axis);
}
else if (curToken == '@')
{
getRawToken();
axis = AXIS_ATTRIBUTE;
unqualifiedStep = parseNodeTest(axis);
}
else if (curToken == OP_ATTRIBUTE)
{
axis = AXIS_ATTRIBUTE;
unqualifiedStep = parseNodeTest(axis);
}
else
{
unqualifiedStep = parseNodeTest(-1);
if (unqualifiedStep != null)
{
axis = AXIS_CHILD;
}
else
{
axis = -1;
unqualifiedStep = parsePrimaryExpr();
}
}
return parseStepQualifiers(unqualifiedStep, axis);
}
Expression parseStepQualifiers(Expression exp, int axis)
throws java.io.IOException, SyntaxException
{
for (;;)
{
if (curToken == '[')
{
int startLine = getLineNumber() + 1;
int startColumn = getColumnNumber() + 1;
int saveSeenPosition = seenPosition;
int saveSawLast = seenLast;
getRawToken();
LambdaExp lexp = new LambdaExp(3);
maybeSetLine(lexp, startLine, startColumn);
Declaration dot = lexp.addDeclaration(DOT_VARNAME);
if (axis >= 0)
dot.setType(NodeType.anyNodeTest);
else
dot.setType(SingletonType.getInstance());
lexp.addDeclaration(POSITION_VARNAME, Type.int_type);
lexp.addDeclaration(LAST_VARNAME, Type.int_type);
comp.push(lexp);
dot.noteValue(null);
Expression cond = parseExprSequence(']', false);
if (curToken == EOF_TOKEN)
eofError("missing ']' - unexpected end-of-file");
char kind;
Procedure valuesFilter;
if (axis < 0)
{
kind = 'P';
valuesFilter = ValuesFilter.exprFilter;
}
else if (axis == AXIS_ANCESTOR || axis == AXIS_ANCESTOR_OR_SELF
|| axis == AXIS_PARENT || axis == AXIS_PRECEDING
|| axis == AXIS_PRECEDING_SIBLING)
{
kind = 'R';
valuesFilter = ValuesFilter.reverseFilter;
}
else
{
kind = 'F';
valuesFilter = ValuesFilter.forwardFilter;
}
/*)
boolean sawPosition = seenPosition > saveSeenPosition;
boolean sawLast = seenLast > saveSeenLast;
*/
maybeSetLine(cond, startLine, startColumn);
comp.pop(lexp);
lexp.body = cond;
getRawToken();
Expression[] args = { exp, lexp };
exp = new ApplyExp(valuesFilter, args);
}
/*
else if (curToken == ARROW_TOKEN)
...;
*/
else
{
return exp;
}
}
}
/**
* Parse a PrimaryExpr.
* @return an Expression.
*/
Expression parsePrimaryExpr()
throws java.io.IOException, SyntaxException
{
Expression exp = parseMaybePrimaryExpr();
if (exp == null)
{
exp = syntaxError("missing expression");
if (curToken != EOF_TOKEN)
getRawToken();
return exp;
}
return exp;
}
void parseEntityOrCharRef ()
throws java.io.IOException, SyntaxException
{
int next = read();
if (next == '#')
{
int base;
next = read();
if (next == 'x')
{
base = 16;
next = read();
}
else
base = 10;
int value = 0;
while (next >= 0)
{
char ch = (char) next;
int digit = Character.digit((char) ch, base);
if (digit < 0)
break;
if (value >= 0x8000000)
break; // Overflow likely.
value = value * base;
value += digit;
next = read();
}
if (next != ';')
{
unread();
error("invalid character reference");
}
// See definition of 'Char' in XML 1.1 2nd ed Specification.
else if ((value > 0 && value <= 0xD7FF)
|| (value >= 0xE000 && value <= 0xFFFD)
|| (value >= 0x10000 && value <= 0x10FFFF))
tokenBufferAppend(value);
else
error('e', "invalid character value "+value, "XQST0090");
}
else
{
int saveLength = tokenBufferLength;
while (next >= 0)
{
char ch = (char) next;
if (! XName.isNamePart(ch))
break;
tokenBufferAppend(ch);
next = read();
}
if (next != ';')
{
unread();
error("invalid entity reference");
return;
}
String ref = new String(tokenBuffer, saveLength,
tokenBufferLength - saveLength);
tokenBufferLength = saveLength;
appendNamedEntity(ref);
}
}
/** Count of enclosed expressions seen in element or attribute content. */
int enclosedExpressionsSeen;
static Expression makeText = makeFunctionExp("gnu.kawa.xml.MakeText",
"makeText");
/** Parse ElementContent (delimiter == '<') or AttributeContent (otherwise).
* @param delimiter is '<' if parsing ElementContent, is either '\'' or
* '\"' if parsing AttributeContent depending on the starting quote
* @param result a buffer to place the resulting Expressions.
*/
void parseContent(char delimiter, Vector result)
throws java.io.IOException, SyntaxException
{
tokenBufferLength = 0;
int startSize = result.size();
int prevEnclosed = startSize - 1;
boolean skipBoundarySpace = ! boundarySpacePreserve && delimiter == '<';
boolean skippable = skipBoundarySpace;
for (;;)
{
int next = read();
if (next == delimiter)
{
if (delimiter == '<')
{
next = read();
Expression text = null;
if (tokenBufferLength > 0)
{
String str = new String(tokenBuffer, 0, tokenBufferLength);
Expression[] args = { new QuoteExp(str) };
text = new ApplyExp(makeText, args);
}
tokenBufferLength = 0;
if (next == '/')
{
if (text != null && ! skippable)
result.addElement(text);
break;
}
Expression content = parseXMLConstructor(next, true);
boolean isCDATA = false;
boolean emptyCDATA = false;
if (content instanceof ApplyExp)
{
ApplyExp aexp = (ApplyExp) content;
if (aexp.getFunction() == makeCDATA)
{
isCDATA = true;
String str = (String) aexp.getArg(0).valueIfConstant();
emptyCDATA = str != null && str.length() == 0;
}
}
if (text != null && (! skippable || isCDATA))
result.addElement(text);
if (isCDATA)
skippable = false;
else
skippable = skipBoundarySpace;
if (! emptyCDATA)
result.addElement(content);
tokenBufferLength = 0;
continue;
}
else if (checkNext(delimiter))
{
tokenBufferAppend(delimiter);
continue;
}
}
if (next == delimiter || next < 0 || next == '{')
{
addText:
{
String text;
if (tokenBufferLength > 0 && ! skippable)
text = new String(tokenBuffer, 0, tokenBufferLength);
else if (next == '{' && prevEnclosed == result.size())
// Handle the <a>{E1}{E2}</a> case - we must insert a
// joiner between E1 ad E2 to avoid a space being inserted.
text = "";
else
break addText; // Don't need to add anything.
Expression[] args = { new QuoteExp(text) };
result.addElement(new ApplyExp(makeText, args));
}
tokenBufferLength = 0;
if (next == delimiter)
break;
else if (next < 0)
eofError("unexpected end-of-file");
else // if (next == '{')
{
next = read();
if (next == '{')
{
tokenBufferAppend('{');
skippable = false;
}
else
{
unread(next);
enclosedExpressionsSeen++;
Expression exp = parseEnclosedExpr();
result.addElement(exp);
tokenBufferLength = 0;
prevEnclosed = result.size();
skippable = skipBoundarySpace;
}
}
}
else if (next == '}')
{
next = read();
if (next == '}')
{
tokenBufferAppend('}');
skippable = false;
}
else
{
error("unexpected '}' in element content");
unread(next);
}
}
else if (next == '&')
{
parseEntityOrCharRef();
skippable = false;
}
else
{
if (delimiter != '<'
&& (next == '\t' || next == '\n' || next == '\r'))
next = ' ';
if (next == '<')
error('e', "'<' must be quoted in a direct attribute value");
if (skippable)
skippable = Character.isWhitespace((char) next);
tokenBufferAppend((char) next);
}
}
}
/** Parse an EnclosedExpr.
* Assume the '{' has been read.
*/
Expression parseEnclosedExpr()
throws java.io.IOException, SyntaxException
{
String saveErrorIfComment = errorIfComment;
errorIfComment = null;
char saveReadState = pushNesting('{');
peekNonSpace("unexpected end-of-file after '{'");
int startLine = getLineNumber() + 1;
int startColumn = getColumnNumber() + 1;
getRawToken();
Expression exp = parseExpr();
for (;;)
{
if (curToken == '}')
break;
if (curToken == EOF_TOKEN || curToken == ')' || curToken == ']')
{
exp = syntaxError("missing '}'");
break;
}
if (curToken != ',')
exp = syntaxError("missing '}' or ','");
else
getRawToken();
exp = makeExprSequence(exp, parseExpr());
}
maybeSetLine(exp, startLine, startColumn);
popNesting(saveReadState);
errorIfComment = saveErrorIfComment;
return exp;
}
/** Coerce the value of an expresison to a boolean value. */
public static Expression booleanValue(Expression exp)
{
Expression[] args = { exp };
Expression string
= makeFunctionExp("gnu.xquery.util.BooleanValue", "booleanValue");
return new ApplyExp(string, args);
}
static final Expression makeCDATA =
makeFunctionExp("gnu.kawa.xml.MakeCDATA", "makeCDATA");
/** Parse an ElementConstructor or other constructs starting with '<'.
* Assume initial '<' has been processed.
* @param next next character (after '<').
*/
Expression parseXMLConstructor (int next, boolean inElementContent)
throws java.io.IOException, SyntaxException
{
Expression exp;
if (next == '!')
{
next = read();
if (next == '-' && peek() == '-')
{
skip();
getDelimited("-->");
boolean bad = false;
int i = tokenBufferLength;
boolean sawHyphen = true;
while (--i >= 0)
{
boolean curHyphen = tokenBuffer[i] == '-';
if (sawHyphen && curHyphen)
{
bad = true;
break;
}
sawHyphen = curHyphen;
}
if (bad)
exp = syntaxError("consecutive or final hyphen in XML comment");
else
{
Expression[] args =
{ new QuoteExp(new String(tokenBuffer, 0, tokenBufferLength)) };
exp = new ApplyExp(makeFunctionExp("gnu.kawa.xml.CommentConstructor",
"commentConstructor"),
args);
}
}
else if (next == '[' && read() == 'C' && read() == 'D'
&& read() == 'A' && read() == 'T' && read() == 'A'
&& read() == '[')
{
if (! inElementContent)
error('e', "CDATA section must be in element content");
getDelimited("]]>");
Expression[] args =
{ new QuoteExp(new String(tokenBuffer, 0, tokenBufferLength)) };
exp = new ApplyExp(makeCDATA, args);
}
else
exp = syntaxError("'<!' must be followed by '--' or '[CDATA['");
}
else if (next == '?')
{
next = peek();
if (next < 0 || ! XName.isNameStart((char) next)
|| getRawToken() != NCNAME_TOKEN)
syntaxError("missing target after '<?'");
String target = new String(tokenBuffer, 0, tokenBufferLength);
int nspaces = 0;
for (;;)
{
int ch = read();
if (ch < 0)
break;
if (! Character.isWhitespace((char) ch))
{
unread();
break;
}
nspaces++;
}
getDelimited("?>");
if (nspaces == 0 && tokenBufferLength > 0)
syntaxError("target must be followed by space or '?>'");
String content = new String(tokenBuffer, 0, tokenBufferLength);
Expression[] args = { new QuoteExp(target), new QuoteExp(content) };
exp = new ApplyExp(makeFunctionExp("gnu.kawa.xml.MakeProcInst",
"makeProcInst"),
args);
}
else if (next < 0 || ! XName.isNameStart((char) next))
exp = syntaxError("expected QName after '<'");
else
{
unread(next);
getRawToken();
char saveReadState = pushNesting('<');
exp = parseElementConstructor();
if (! inElementContent)
exp = wrapWithBaseUri(exp);
popNesting(saveReadState);
}
return exp;
}
/** Generate code to cast argument to a QName
* (which is implemented using <code>Symbol</code>). */
static ApplyExp castQName (Expression value)
{
return new ApplyExp(new ReferenceExp(XQResolveNames.xsQNameDecl),
new Expression[] { value });
}
/** Parse ElementConstructor.
* Assume initial {@code '<'} has been processed,
* and we're looking at the next token..
* Reads through end of the end tag. FIXME
*/
Expression parseElementConstructor()
throws java.io.IOException, SyntaxException
{
// Note that we cannot do namespace resolution at parse time,
// because of constructs like this: <a x="{$x:v}" xmlns:x="xx"/>
// Instead we defer namespaced lookup until XQResolveNames. (Mostly -
// some places still incorrectly do premature namespace resolution.)
String startTag = new String(tokenBuffer, 0, tokenBufferLength);
Vector vec = new Vector();
Expression[] args;
vec.addElement(castQName(new QuoteExp(startTag)));
errorIfComment = "comment not allowed in element start tag";
NamespaceBinding nsBindings = null;
int ch;
for (;;)
{
ch = skipSpace();
if (ch < 0 || ch == '>' || ch == '/')
break;
unread(ch);
getRawToken();
int vecSize = vec.size();
if (curToken != NCNAME_TOKEN && curToken != QNAME_TOKEN)
break;
String attrName = new String(tokenBuffer, 0, tokenBufferLength);
int startLine = getLineNumber() + 1;
int startColumn = getColumnNumber() + 1 - tokenBufferLength;
String definingNamespace = null;
if (curToken == NCNAME_TOKEN)
{
if (attrName.equals("xmlns"))
definingNamespace = "";
}
else
{
if (attrName.startsWith("xmlns:"))
definingNamespace = attrName.substring(6).intern();
}
Expression makeAttr
= definingNamespace != null ? null
: MakeAttribute.makeAttributeExp;
vec.addElement(castQName(new QuoteExp(attrName)));
ch = skipSpace();
if (ch != '=')
{
errorIfComment = null;
return syntaxError("missing '=' after attribute");
}
ch = skipSpace();
int enclosedExpressionsStart = enclosedExpressionsSeen;
if (ch == '{')
{
warnOldVersion("enclosed attribute value expression should be quoted");
vec.addElement(parseEnclosedExpr());
}
else
parseContent((char) ch, vec);
int n = vec.size() - vecSize;
if (definingNamespace != null)
{
String ns = "";
if (n == 1)
ns = "";
else if (enclosedExpressionsSeen > enclosedExpressionsStart)
syntaxError("enclosed expression not allowed in namespace declaration");
else
{
Object x = vec.elementAt(vecSize+1);
ApplyExp ax;
if (x instanceof ApplyExp
&& (ax = (ApplyExp) x).getFunction() == makeText)
x = ax.getArg(0);
ns = ((QuoteExp) x).getValue()
.toString().intern();
}
vec.setSize(vecSize);
checkAllowedNamespaceDeclaration(definingNamespace, ns, true);
if (definingNamespace == "")
definingNamespace = null;
for (NamespaceBinding nsb = nsBindings;
nsb != null; nsb = nsb.getNext())
{
if (nsb.getPrefix() == definingNamespace)
{
error('e',
definingNamespace == null
? "duplicate default namespace declaration"
: "duplicate namespace prefix '"+definingNamespace+'\'',
"XQST0071");
break;
}
}
nsBindings
= new NamespaceBinding(definingNamespace,
ns == "" ? null : ns,
nsBindings);
}
else
{
args = new Expression[n];
for (int i = n; --i >= 0; )
args[i] = (Expression) vec.elementAt(vecSize + i);
vec.setSize(vecSize);
ApplyExp aexp = new ApplyExp(makeAttr, args);
maybeSetLine(aexp, startLine, startColumn);
vec.addElement(aexp);
}
}
errorIfComment = null;
boolean empty = false;
if (ch == '/')
{
ch = read();
if (ch == '>')
empty = true;
else
unread(ch);
}
if (! empty)
{
if (ch != '>')
return syntaxError("missing '>' after start element");
parseContent('<', vec);
ch = peek();
if (ch >= 0)
{
if (! XName.isNameStart((char) ch))
return syntaxError("invalid tag syntax after '</'");
getRawToken();
String tag = new String(tokenBuffer, 0, tokenBufferLength);
if (! (tag.equals(startTag)))
return syntaxError("'<"+startTag+">' closed by '</"+tag+">'");
errorIfComment = "comment not allowed in element end tag";
ch = skipSpace();
errorIfComment = null;
}
if (ch != '>')
return syntaxError("missing '>' after end element");
}
args = new Expression[vec.size()];
vec.copyInto(args);
MakeElement mkElement = new MakeElement();
mkElement.copyNamespacesMode = copyNamespacesMode;
// Ths is just the chain of NamespaceBindings for namespace declaration
// attributes from this immediate constructor. At resolve time we chain
// this list onto the list from outer element constructors.
mkElement.setNamespaceNodes(nsBindings);
Expression result = new ApplyExp(new QuoteExp(mkElement), args);
return result;
}
Expression wrapWithBaseUri (Expression exp)
{
if (getStaticBaseUri() == null)
return exp;
return new ApplyExp(MakeWithBaseUri.makeWithBaseUri,
new Expression[] {
new ApplyExp(new ReferenceExp(XQResolveNames.staticBaseUriDecl), Expression.noExpressions),
exp })
.setLine(exp);
}
/** Parse ParenthesizedExpr.
*.When called, curToken should be pointing at a '(',
* or a token which ends if a '(', such as IF_LPAREN_TOKEN.
*/
Expression parseParenExpr ()
throws java.io.IOException, SyntaxException
{
getRawToken();
char saveReadState = pushNesting('(');
Expression exp = parseExprSequence(')', true);
popNesting(saveReadState);
if (curToken == EOF_TOKEN)
eofError("missing ')' - unexpected end-of-file");
return exp;
}
Expression parseExprSequence(int rightToken, boolean optional)
throws java.io.IOException, SyntaxException
{
if (curToken == rightToken || curToken == EOF_TOKEN)
{
if (! optional)
syntaxError("missing expression");
return QuoteExp.voidExp;
}
Expression exp = null;
for (;;)
{
Expression exp1 = parseExprSingle();
exp = exp == null ? exp1 : makeExprSequence(exp, exp1);
if (curToken == rightToken || curToken == EOF_TOKEN)
break;
if (nesting == 0 && curToken == EOL_TOKEN)
return exp;
if (curToken != ',')
return syntaxError (rightToken == ')' ? "expected ')'"
: "confused by syntax error");
getRawToken();
}
return exp;
}
Expression parseTypeSwitch()
throws java.io.IOException, SyntaxException
{
char save = pushNesting('t');
Expression selector = parseParenExpr();
getRawToken();
Object varName = null;
Declaration decl;
Vector vec = new Vector();
vec.addElement(selector);
while (match("case"))
{
pushNesting('c');
getRawToken();
if (curToken == '$')
{
decl = parseVariableDeclaration();
if (decl == null)
return syntaxError("missing Variable after '$'");
getRawToken();
if (match("as"))
getRawToken();
else
error('e', "missing 'as'");
}
else
decl = new Declaration("(arg)");
decl.setTypeExp(parseDataType());
popNesting('t');
LambdaExp lexp = new LambdaExp(1);
lexp.addDeclaration(decl);
if (match("return"))
getRawToken();
else
error("missing 'return' after 'case'");
comp.push(lexp);
pushNesting('r');
Expression caseExpr = parseExpr();
lexp.body = caseExpr;
popNesting('t');
comp.pop(lexp);
vec.addElement(lexp);
}
if (match("default"))
{
LambdaExp lexp = new LambdaExp(1);
getRawToken();
if (curToken == '$')
{
decl = parseVariableDeclaration();
if (decl == null)
return syntaxError("missing Variable after '$'");
getRawToken();
}
else
decl = new Declaration("(arg)");
lexp.addDeclaration(decl);
if (match("return"))
getRawToken();
else
error("missing 'return' after 'default'");
comp.push(lexp);
Expression defaultExpr = parseExpr();
lexp.body = defaultExpr;
comp.pop(lexp);
vec.addElement(lexp);
}
else
{
error(comp.isPedantic() ? 'e' : 'w',
"no 'default' clause in 'typeswitch'",
"XPST0003");
}
popNesting(save);
Expression[] args = new Expression[vec.size()];
vec.copyInto(args);
return new ApplyExp(makeFunctionExp("gnu.kawa.reflect.TypeSwitch",
"typeSwitch"),
args);
}
/**
* Try to parse a PrimaryExpr.
* @return an Expression, or null if no PrimaryExpr was seen.
*/
Expression parseMaybePrimaryExpr()
throws java.io.IOException, SyntaxException
{
int startLine = curLine;
int startColumn = curColumn;
int token = peekOperand();
Expression exp;
int c1, c2, c3;
Vector vec;
Expression[] args;
switch (token)
{
case '(':
exp = parseParenExpr();
break;
case PRAGMA_START_TOKEN:
Stack extArgs = new Stack();
for (;;)
{
getRawToken();
Expression qname;
if (curToken != QNAME_TOKEN && curToken != NCNAME_TOKEN)
qname = syntaxError("missing pragma name");
else
qname = QuoteExp.getInstance(new String(tokenBuffer, 0, tokenBufferLength));
extArgs.push(qname);
StringBuffer sbuf = new StringBuffer();
int ch;
int spaces = -1;
do { ch = read(); spaces++; }
while (ch >= 0 && Character.isWhitespace((char) ch));
while (ch != '#' || peek() != ')')
{
if (ch < 0)
eofError("pragma ended by end-of-file");
if (spaces == 0)
error("missing space between pragma and extension content");
spaces = 1;
sbuf.append((char) ch);
ch = read();
}
read(); // skip ')'
extArgs.push(QuoteExp.getInstance(sbuf.toString()));
getRawToken();
if (curToken != PRAGMA_START_TOKEN)
break;
}
if (curToken == '{')
{
getRawToken();
if (curToken != '}')
{
char saveReadState = pushNesting('{');
extArgs.push(parseExprSequence('}', false));
popNesting(saveReadState);
if (curToken == EOF_TOKEN)
eofError("missing '}' - unexpected end-of-file");
}
args = new Expression[extArgs.size()];
extArgs.copyInto(args);
exp = new ApplyExp(new ReferenceExp(XQResolveNames.handleExtensionDecl), args);
}
else
exp = syntaxError("missing '{' after pragma");
break;
case '{':
exp = syntaxError("saw unexpected '{' - assume you meant '('");
parseEnclosedExpr();
break;
case OP_LSS:
int next = read();
if (next == '/')
{
getRawToken();
String msg;
if (curToken == NCNAME_TOKEN || curToken == QNAME_TOKEN
|| curToken == NCNAME_COLON_TOKEN)
msg = "saw end tag '</" + new String(tokenBuffer, 0, tokenBufferLength) + ">' not in an element constructor";
else
msg = "saw end tag '</' not in an element constructor";
curLine = startLine;
curColumn = startColumn;
exp = syntaxError(msg);
while (curToken != OP_GRT && curToken != EOF_TOKEN && curToken != EOL_TOKEN)
getRawToken();
return exp;
}
else
{
exp = parseXMLConstructor(next, false);
maybeSetLine(exp, startLine, startColumn);
}
break;
case STRING_TOKEN:
exp = new QuoteExp(new String(tokenBuffer, 0, tokenBufferLength).intern());
break;
case INTEGER_TOKEN:
exp = new QuoteExp(IntNum.valueOf(tokenBuffer, 0, tokenBufferLength,
10, false));
break;
case DECIMAL_TOKEN:
case DOUBLE_TOKEN:
String str = new String(tokenBuffer, 0, tokenBufferLength);
try
{
Object val;
if (token == DECIMAL_TOKEN)
val = new java.math.BigDecimal(str);
else
val = new java.lang.Double(str);
exp = new QuoteExp(val);
}
catch (Throwable ex)
{
exp = syntaxError("invalid decimal literal: '"+str+"'");
}
break;
case '$':
Object name = parseVariable();
if (name == null)
return syntaxError("missing Variable");
exp = new ReferenceExp(name);
break;
case FNAME_TOKEN:
name = new String(tokenBuffer, 0, tokenBufferLength);
char save = pushNesting('(');
getRawToken();
vec = new Vector(10);
if (curToken != ')')
{
for (;;)
{
Expression arg = parseExpr();
vec.addElement(arg);
if (curToken == ')')
break;
if (curToken != ',')
return syntaxError("missing ')' after function call");
getRawToken();
}
}
args = new Expression[vec.size()];
vec.copyInto(args);
ReferenceExp rexp = new ReferenceExp(name, null);
rexp.setProcedureName(true);
exp = new ApplyExp(rexp, args);
maybeSetLine(exp, startLine, startColumn);
popNesting(save);
break;
case ELEMENT_TOKEN:
case ATTRIBUTE_TOKEN:
case COMMENT_TOKEN:
case DOCUMENT_TOKEN:
case TEXT_TOKEN:
case PI_TOKEN:
getRawToken(); // Skip 'element'.
vec = new Vector();
Expression func;
if (token == ELEMENT_TOKEN || token == ATTRIBUTE_TOKEN)
{
Expression element;
if (curToken == NCNAME_TOKEN || curToken == QNAME_TOKEN)
element = parseNameTest(token != ELEMENT_TOKEN);
else if (curToken == '{')
element = parseEnclosedExpr();
else
return syntaxError("missing element/attribute name");
vec.addElement(castQName(element));
if (token == ELEMENT_TOKEN)
{
MakeElement mk = new MakeElement();
mk.copyNamespacesMode = copyNamespacesMode;
func = new QuoteExp(mk);
}
else
func = MakeAttribute.makeAttributeExp;
getRawToken();
}
else if (token == DOCUMENT_TOKEN)
func = makeFunctionExp("gnu.kawa.xml.DocumentConstructor",
"documentConstructor");
else if (token == COMMENT_TOKEN)
func = makeFunctionExp("gnu.kawa.xml.CommentConstructor",
"commentConstructor");
else if (token == PI_TOKEN)
{
Expression target;
if (curToken == NCNAME_TOKEN)
target = new QuoteExp(new String(tokenBuffer, 0,
tokenBufferLength).intern());
else if (curToken == '{')
{
target = parseEnclosedExpr();
}
else
{
target = syntaxError("expected NCName or '{' after 'processing-instruction'");
if (curToken != QNAME_TOKEN)
return target;
}
vec.addElement(target);
func = makeFunctionExp("gnu.kawa.xml.MakeProcInst",
"makeProcInst");
getRawToken();
}
else /* token == TEXT_TOKEN */
func = makeFunctionExp("gnu.kawa.xml.MakeText",
"makeText");
char saveReadState = pushNesting('{');
peekNonSpace("unexpected end-of-file after '{'");
if (curToken != '{')
return syntaxError("missing '{'");
getRawToken();
if (token == TEXT_TOKEN || token == COMMENT_TOKEN
|| token == PI_TOKEN)
vec.addElement(parseExprSequence('}', token == PI_TOKEN));
else if (curToken != '}')
{
vec.addElement(parseExpr());
while (curToken == ',')
{
getRawToken();
vec.addElement(parseExpr());
}
}
popNesting(saveReadState);
if (curToken != '}')
return syntaxError("missing '}'");
args = new Expression[vec.size()];
vec.copyInto(args);
exp = new ApplyExp(func, args);
maybeSetLine(exp, startLine, startColumn);
if (token == DOCUMENT_TOKEN || token == ELEMENT_TOKEN)
exp = wrapWithBaseUri(exp);
break;
case ORDERED_LBRACE_TOKEN:
case UNORDERED_LBRACE_TOKEN:
getRawToken();
exp = parseExprSequence('}', false);
break;
default:
return null;
}
/*
if (nesting == 0)
{
int ch = skipSpace(false);
if (ch < 0 || ch == '\n' || ch == '\r')
return exp;
unread(ch);
}
*/
getRawToken();
return exp;
}
public Expression parseIfExpr()
throws java.io.IOException, SyntaxException
{
char saveReadState1 = pushNesting('i');
getRawToken();
char saveReadState2 = pushNesting('(');
Expression cond = parseExprSequence(')', false);
popNesting(saveReadState2);
if (curToken == EOF_TOKEN)
eofError("missing ')' - unexpected end-of-file");
getRawToken();
if (! match("then"))
syntaxError("missing 'then'");
else
getRawToken();
Expression thenPart = parseExpr();
if (! match("else"))
syntaxError("missing 'else'");
else
getRawToken();
popNesting(saveReadState1);
Expression elsePart = parseExpr();
return new IfExp(booleanValue(cond), thenPart, elsePart);
}
public boolean match(String word)
{
if (curToken != NCNAME_TOKEN)
return false;
int len = word.length();
if (tokenBufferLength != len)
return false;
for (int i = len; --i >= 0; )
{
char cs = word.charAt(i);
char cb = tokenBuffer[i];
if (cs != cb)
return false;
}
return true;
}
/** Parse a Variable. */
public Object parseVariable ()
throws java.io.IOException, SyntaxException
{
if (curToken == '$')
getRawToken();
else
syntaxError("missing '$' before variable name");
String str = new String(tokenBuffer, 0, tokenBufferLength);
// Note we cannot do namespace resolution here - see comment in
// parseElementConstructor.
if (curToken == QNAME_TOKEN)
return str;
else if (curToken == NCNAME_TOKEN)
return Namespace.EmptyNamespace.getSymbol(str.intern());
else
return null;
}
public Declaration parseVariableDeclaration ()
throws java.io.IOException, SyntaxException
{
Object name = parseVariable();
if (name == null)
return null;
Declaration decl = new Declaration(name);
maybeSetLine(decl, getLineNumber() + 1,
getColumnNumber() + 1 - tokenBufferLength);
return decl;
}
public Expression parseFLWRExpression (boolean isFor)
throws java.io.IOException, SyntaxException
{
int flworDeclsSave = flworDeclsFirst;
flworDeclsFirst = flworDeclsCount;
Expression exp = parseFLWRInner(isFor);
if (match ("order"))
{
getRawToken();
if (match ("by"))
getRawToken();
else
error("missing 'by' following 'order'");
Stack specs = new Stack();
for (;;)
{
boolean descending = false;
char emptyOrder = defaultEmptyOrder;
LambdaExp lexp = new LambdaExp(flworDeclsCount-flworDeclsFirst);
for (int i = flworDeclsFirst; i < flworDeclsCount; i++)
lexp.addDeclaration(flworDecls[i].getSymbol());
comp.push(lexp);
lexp.body = parseExprSingle();
comp.pop(lexp);
specs.push(lexp);
if (match("ascending"))
getRawToken();
else if (match("descending"))
{
getRawToken();
descending = true;
}
if (match("empty"))
{
getRawToken();
if (match("greatest"))
{
getRawToken();
emptyOrder = 'G';
}
else if (match("least"))
{
getRawToken();
emptyOrder = 'L';
}
else
error
("'empty' sequence order must be 'greatest' or 'least'");
}
specs.push(new QuoteExp((descending ? "D" : "A") + emptyOrder));
Object collation = defaultCollator;
if (match("collation"))
{
Object uri = parseURILiteral();
if (uri instanceof String)
{
try
{
String uriString = resolveAgainstBaseUri((String) uri);
collation = NamedCollator.make(uriString);
}
catch (Exception name)
{
error('e', "unknown collation '"+uri+"'", "XQST0076");
}
}
getRawToken();
}
specs.push(new QuoteExp(collation));
if (curToken != ',')
break;
getRawToken();
}
if (! match("return"))
return syntaxError("expected 'return' clause");
getRawToken();
LambdaExp lexp = new LambdaExp(flworDeclsCount-flworDeclsFirst);
//maybeSetLine(lexp, declLine, declColumn);
for (int i = flworDeclsFirst; i < flworDeclsCount; i++)
lexp.addDeclaration(flworDecls[i].getSymbol());
comp.push(lexp);
lexp.body = parseExprSingle();
comp.pop(lexp);
int nspecs = specs.size();
Expression[] args = new Expression[2 + nspecs];
args[0] = exp;
args[1] = lexp;
for (int i = 0; i < nspecs; i++)
args[2+i] = (Expression) specs.elementAt(i);
return new ApplyExp(makeFunctionExp("gnu.xquery.util.OrderedMap",
"orderedMap"),
args);
}
flworDeclsCount = flworDeclsFirst;
flworDeclsFirst = flworDeclsSave;
return exp;
}
/** Parse a let- or a for-expression.
* Assume the 'let'/'for'-token has been seen, and we've read '$'.
*
* If we see the 'order' keyword of an 'order by' clause then we stop
* parsing, and return a result as if we instead saw a
* 'return make-tuple($x, ...)'. The 'order by' clause will get
* parsed by the outer-most 'for' or 'let'.
*/
public Expression parseFLWRInner (boolean isFor)
throws java.io.IOException, SyntaxException
{
char saveNesting = pushNesting(isFor ? 'f' : 'l');
curToken = '$';
Declaration decl = parseVariableDeclaration();
if (decl == null)
return syntaxError("missing Variable - saw "+tokenString());
if (flworDecls == null)
flworDecls = new Declaration[8];
else if (flworDeclsCount >= flworDecls.length)
{
Declaration[] tmp = new Declaration[2 * flworDeclsCount];
System.arraycopy(flworDecls, 0, tmp, 0, flworDeclsCount);
flworDecls = tmp;
}
flworDecls[flworDeclsCount++] = decl;
getRawToken();
Expression type = parseOptionalTypeDeclaration();
ScopeExp sc;
Expression[] inits = new Expression[1];
Declaration posDecl = null;
if (isFor)
{
boolean sawAt = match("at");
LambdaExp lexp = new LambdaExp(sawAt ? 2 : 1);
if (sawAt)
{
getRawToken();
if (curToken == '$')
{
posDecl = parseVariableDeclaration();
getRawToken();
}
if (posDecl == null)
syntaxError("missing Variable after 'at'");
}
sc = lexp;
if (match("in"))
getRawToken();
else
{
if (curToken == COLON_EQUAL_TOKEN)
getRawToken();
syntaxError("missing 'in' in 'for' clause");
}
}
else
{
if (curToken == COLON_EQUAL_TOKEN)
getRawToken();
else
{
if (match("in"))
getRawToken();
syntaxError("missing ':=' in 'let' clause");
}
LetExp let = new LetExp(inits);
sc = let;
}
inits[0] = parseExprSingle();
if (type != null && ! isFor) // FIXME - for now
inits[0] = Convert.makeCoercion(inits[0], type);
popNesting(saveNesting);
comp.push(sc);
sc.addDeclaration(decl);
decl.setTypeExp(type);
if (isFor)
{
decl.noteValue (null); // Does not have a known value.
decl.setFlag(Declaration.IS_SINGLE_VALUE);
}
if (posDecl != null)
{
sc.addDeclaration(posDecl);
posDecl.setType(LangPrimType.intType);
posDecl.noteValue(null);
posDecl.setFlag(Declaration.IS_SINGLE_VALUE);
}
Expression body;
if (curToken == ',')
{
getRawToken();
if (curToken != '$')
return syntaxError("missing $NAME after ','");
body = parseFLWRInner(isFor);
}
else if (match("for"))
{
getRawToken();
if (curToken != '$')
return syntaxError("missing $NAME after 'for'");
body = parseFLWRInner(true);
}
else if (match("let"))
{
getRawToken();
if (curToken != '$')
return syntaxError("missing $NAME after 'let'");
body = parseFLWRInner(false);
}
else
{
Expression cond;
char save = pushNesting('w');
if (curToken == OP_WHERE)
{
getRawToken();
cond = parseExprSingle();
}
else if (match("where"))
{
cond = parseExprSingle();
}
else
cond = null;
popNesting(save);
boolean sawStable = match("stable");
if (sawStable)
getRawToken();
boolean sawReturn = match("return");
boolean sawOrder = match("order");
if (! sawReturn && ! sawOrder && ! match("let") && ! match("for"))
return syntaxError("missing 'return' clause");
if (! sawOrder)
peekNonSpace("unexpected eof-of-file after 'return'");
int bodyLine = getLineNumber() + 1;
int bodyColumn = getColumnNumber() + 1;
if (sawReturn)
getRawToken();
if (sawOrder)
{
int ndecls = flworDeclsCount - flworDeclsFirst;
Expression[] args = new Expression[ndecls];
for (int i = 0; i < ndecls; i++)
args[i] = new ReferenceExp(flworDecls[flworDeclsFirst+i]);
body = new ApplyExp(new PrimProcedure("gnu.xquery.util.OrderedMap",
"makeTuple$V", 1),
args);
}
else
body = parseExprSingle();
if (cond != null)
body = new IfExp(booleanValue(cond), body, QuoteExp.voidExp);
maybeSetLine(body, bodyLine, bodyColumn);
}
comp.pop(sc);
if (isFor)
{
LambdaExp lexp = (LambdaExp) sc;
lexp.body = body;
Expression[] args = { sc, inits[0]}; // SIC
return new ApplyExp(makeFunctionExp("gnu.kawa.functions.ValuesMap",
lexp.min_args == 1 ? "valuesMap"
: "valuesMapWithPos"),
args);
}
else
((LetExp) sc).setBody(body);
return sc;
}
/** Parse a some- or an every-expression.
* Assume the 'some'/'every'-token has been seen, and we've read '$'. */
public Expression parseQuantifiedExpr (boolean isEvery)
throws java.io.IOException, SyntaxException
{
char saveNesting = pushNesting(isEvery ? 'e' : 's');
curToken = '$';
Declaration decl = parseVariableDeclaration();
if (decl == null)
return syntaxError("missing Variable token:"+curToken);
getRawToken();
LambdaExp lexp = new LambdaExp(1);
lexp.addDeclaration(decl);
decl.noteValue (null); // Does not have a known value.
decl.setFlag(Declaration.IS_SINGLE_VALUE);
decl.setTypeExp(parseOptionalTypeDeclaration());
if (match("in"))
getRawToken();
else
{
if (curToken == COLON_EQUAL_TOKEN)
getRawToken();
syntaxError("missing 'in' in QuantifiedExpr");
}
Expression[] inits = { parseExprSingle() };
popNesting(saveNesting);
comp.push(lexp);
Expression body;
if (curToken == ',')
{
getRawToken();
if (curToken != '$')
return syntaxError("missing $NAME after ','");
body = parseQuantifiedExpr(isEvery);
}
else
{
boolean sawSatisfies = match("satisfies");
if (! sawSatisfies && ! match("every") && ! match("some"))
return syntaxError("missing 'satisfies' clause");
peekNonSpace("unexpected eof-of-file after 'satisfies'");
int bodyLine = getLineNumber() + 1;
int bodyColumn = getColumnNumber() + 1;
if (sawSatisfies)
getRawToken();
body = parseExprSingle();
maybeSetLine(body, bodyLine, bodyColumn);
}
comp.pop(lexp);
lexp.body = body;
Expression[] args = { lexp, inits[0]}; // SIC
return new ApplyExp(makeFunctionExp("gnu.xquery.util.ValuesEvery",
isEvery ? "every" : "some"),
args);
}
public Expression parseFunctionDefinition(int declLine, int declColumn)
throws java.io.IOException, SyntaxException
{
if (curToken != QNAME_TOKEN && curToken != NCNAME_TOKEN)
return syntaxError("missing function name");
String name = new String(tokenBuffer, 0, tokenBufferLength);
Symbol sym = namespaceResolve(name, true);
String uri = sym.getNamespaceURI();
if (uri == NamespaceBinding.XML_NAMESPACE
|| uri == XQuery.SCHEMA_NAMESPACE
|| uri == XQuery.SCHEMA_INSTANCE_NAMESPACE
|| uri == XQuery.XQUERY_FUNCTION_NAMESPACE)
{
error('e',
"cannot declare function in standard namespace '"+uri+'\'',
"XQST0045");
}
else if (uri == "")
{
error(comp.isPedantic() ? 'e' : 'w',
"cannot declare function in empty namespace",
"XQST0060");
}
else if (libraryModuleNamespace != null && uri != libraryModuleNamespace
&& (! XQuery.LOCAL_NAMESPACE.equals(uri) || comp.isPedantic()))
{
error('e', "function not in namespace of library module", "XQST0048");
}
getRawToken();
if (curToken != '(')
return syntaxError("missing parameter list:"+curToken);
getRawToken();
LambdaExp lexp = new LambdaExp();
maybeSetLine(lexp, declLine, declColumn);
lexp.setName(name);
Declaration decl = comp.currentScope().addDeclaration(sym);
if (comp.isStatic())
decl.setFlag(Declaration.STATIC_SPECIFIED);
lexp.setFlag(LambdaExp.OVERLOADABLE_FIELD);
decl.setCanRead(true);
decl.setProcedureDecl(true);
maybeSetLine(decl, declLine, declColumn);
comp.push(lexp);
if (curToken != ')')
{
paramLoop:
for (;;)
{
Declaration param = parseVariableDeclaration();
if (param == null)
error("missing parameter name");
else
{
lexp.addDeclaration(param);
getRawToken();
lexp.min_args++;
lexp.max_args++;
param.setTypeExp(parseOptionalTypeDeclaration());
}
if (curToken == ')')
break;
if (curToken != ',')
{
Expression err = syntaxError("missing ',' in parameter list");
for (;;)
{
getRawToken();
if (curToken < 0 || curToken == ';' || curToken == ';')
return err;
if (curToken == ')')
break paramLoop;
if (curToken == ',')
break;
}
}
else
getRawToken();
}
}
getRawToken();
Expression retType = parseOptionalTypeDeclaration ();
lexp.body = parseEnclosedExpr();
comp.pop(lexp);
if (retType != null)
Convert.setCoercedReturnValue(lexp, retType, interpreter);
SetExp sexp = new SetExp(decl, lexp);
sexp.setDefining (true);
decl.noteValue(lexp);
return sexp;
}
public Object readObject ()
throws java.io.IOException, SyntaxException
{
return parse(null);
}
Compilation comp;
String defaultElementNamespace = "";
/** Chain of namespace bindings from namespace declaration attributes
* in outer direct element constructors. This is only non-empty during
* resolve time, but it is declared here so namespaceResolve can use it. */
NamespaceBinding constructorNamespaces = NamespaceBinding.predefinedXML;
/** Chain of namespace bindings from declarations in prolog,
* followed by the builtinNamespaces. */
NamespaceBinding prologNamespaces;
static NamespaceBinding builtinNamespaces;
static {
NamespaceBinding ns = NamespaceBinding.predefinedXML;
ns = new NamespaceBinding("xml", NamespaceBinding.XML_NAMESPACE, ns);
ns = new NamespaceBinding("xs", XQuery.SCHEMA_NAMESPACE, ns);
ns = new NamespaceBinding("xsi", XQuery.SCHEMA_INSTANCE_NAMESPACE, ns);
ns = new NamespaceBinding("fn", XQuery.XQUERY_FUNCTION_NAMESPACE, ns);
ns = new NamespaceBinding("html", XQuery.XHTML_NAMESPACE, ns);
ns = new NamespaceBinding("kawa", XQuery.KAWA_FUNCTION_NAMESPACE, ns);
ns = new NamespaceBinding("qexo", XQuery.QEXO_FUNCTION_NAMESPACE, ns);
ns = new NamespaceBinding("local", XQuery.LOCAL_NAMESPACE, ns);
builtinNamespaces = ns;
}
protected Symbol namespaceResolve (String name, boolean function)
{
int colon = name.indexOf(':');
String prefix = colon >= 0 ? name.substring(0, colon).intern()
: function ? XQuery.DEFAULT_FUNCTION_PREFIX
: XQuery.DEFAULT_ELEMENT_PREFIX;
String uri = QNameUtils.lookupPrefix(prefix, constructorNamespaces,
prologNamespaces);
if (uri == null)
{
if (colon < 0)
uri = "";
else if (! comp.isPedantic())
{
try
{
Class cl = Class.forName(prefix);
uri = "class:" + prefix;
}
catch (Exception ex)
{
uri = null;
}
}
if (uri == null)
{
error('e',
"unknown namespace prefix '" + prefix + "'",
"XPST0081");
uri = "(unknown namespace)";
}
}
String local = colon < 0 ? name : name.substring(colon+1);
return Symbol.make((String) uri, local, prefix);
}
void parseSeparator ()
throws java.io.IOException, SyntaxException
{
int startLine = port.getLineNumber() + 1;
int startColumn = port.getColumnNumber() + 1;
int next = skipSpace(nesting != 0);
if (next == ';')
return;
if (warnOldVersion && next != '\n')
{
curLine = startLine;
curColumn = startColumn;
error('w', "missing ';' after declaration");
}
if (next >= 0)
unread(next);
}
public static final QuoteExp getExternalFunction =
QuoteExp.getInstance(new PrimProcedure("gnu.xquery.lang.XQuery",
"getExternal", 2));
/** Parse an expression.
* Return null on EOF. */
public Expression parse(Compilation comp)
throws java.io.IOException, SyntaxException
{
this.comp = comp;
int ch = skipSpace();
if (ch < 0)
return null;
parseCount++;
unread(ch);
int startLine = getLineNumber() + 1;
int startColumn = getColumnNumber() + 1;
// Handle Unix #!PROGRAM convention. */
if (ch == '#' && startLine == 1 && startColumn == 1)
{
read();
if ((ch = read()) != '!' || (ch = read()) != '/')
error("'#' is only allowed in initial '#!/PROGRAM'");
while (ch != '\r' && ch != '\n' && ch >= 0)
ch = read();
}
if (getRawToken() == EOF_TOKEN)
return null;
peekOperand();
if (curToken == NCNAME_TOKEN
&& "namespace".equals((String) curValue))
{
if (warnOldVersion)
error('w', "use 'declare namespace' instead of 'namespace'");
curToken = DECLARE_NAMESPACE_TOKEN;
}
int declLine, declColumn, next;
Declaration decl;
String prefix, uri;
Object val;
Expression exp;
switch (curToken)
{
case DEFINE_QNAME_TOKEN:
declLine = getLineNumber() + 1;
declColumn = getColumnNumber() + 1;
next = peekNonSpace("unexpected end-of-file after 'define QName'");
if (next == '(')
{
syntaxError("'missing 'function' after 'define'");
curToken = NCNAME_TOKEN;
return parseFunctionDefinition(declLine, declColumn);
}
else
return syntaxError("missing keyword after 'define'");
case DECLARE_FUNCTION_TOKEN:
declLine = getLineNumber() + 1;
declColumn = getColumnNumber() + 1;
getRawToken();
peekNonSpace("unexpected end-of-file after 'define function'");
char save = pushNesting('d');
exp = parseFunctionDefinition(declLine, declColumn);
popNesting(save);
parseSeparator();
maybeSetLine(exp, startLine, startColumn);
seenDeclaration = true;
return exp;
case DECLARE_VARIABLE_TOKEN:
getRawToken();
decl = parseVariableDeclaration();
if (decl == null)
return syntaxError("missing Variable");
Object name = decl.getSymbol();
if (name instanceof String)
decl.setSymbol(namespaceResolve((String) name, false));
if (libraryModuleNamespace != null)
{
uri = ((Symbol) decl.getSymbol()).getNamespaceURI();
if (uri != libraryModuleNamespace
&& (! XQuery.LOCAL_NAMESPACE.equals(uri) || comp.isPedantic()))
error('e', "variable not in namespace of library module"
, "XQST0048");
}
comp.currentScope().addDeclaration(decl);
getRawToken();
Expression type = parseOptionalTypeDeclaration();
decl.setCanRead(true);
//decl.setFlag(Declaration.NONSTATIC_SPECIFIED);
decl.setFlag(Declaration.IS_CONSTANT);
Expression init = null;
boolean sawEq = false;
if (curToken == OP_EQU || curToken == COLON_EQUAL_TOKEN)
{
if (curToken==OP_EQU)
error("declare variable contains '=' instead of ':='");
getRawToken();
sawEq = true;
}
if (curToken == '{')
{
warnOldVersion("obsolete '{' in variable declaration");
init = parseEnclosedExpr();
parseSeparator();
}
else if (match("external"))
{
Expression[] args =
{
castQName(new QuoteExp(decl.getSymbol())),
type==null ? QuoteExp.nullExp : type
};
init = new ApplyExp(getExternalFunction, args);
maybeSetLine(init, curLine, curColumn);
getRawToken();
}
else
{
init = parseExpr();
Expression err = null;
if (! sawEq || init == null)
err = syntaxError("expected ':= init' or 'external'");
if (init == null)
init = err;
}
if (type != null)
init = Convert.makeCoercion(init, type);
decl.noteValue(init);
exp = SetExp.makeDefinition(decl, init);
maybeSetLine(exp, startLine, startColumn);
seenDeclaration = true;
return exp;
case DECLARE_NAMESPACE_TOKEN:
case MODULE_NAMESPACE_TOKEN:
int command = curToken;
if (command == MODULE_NAMESPACE_TOKEN
&& libraryModuleNamespace != null )
error('e', "duplicate module declaration");
else if (seenDeclaration && ! interactive)
error('e', "namespace declared after function/variable/option");
next = skipSpace(nesting != 0);
if (next >= 0)
{
unread();
if (XName.isNameStart((char) next))
{
getRawToken();
if (curToken != NCNAME_TOKEN)
return syntaxError("missing namespace prefix");
prefix = new String(tokenBuffer, 0, tokenBufferLength);
getRawToken();
if (curToken != OP_EQU)
return syntaxError("missing '=' in namespace declaration");
getRawToken();
if (curToken != STRING_TOKEN)
return syntaxError("missing uri in namespace declaration");
uri = new String(tokenBuffer, 0, tokenBufferLength).intern();
prefix = prefix.intern();
for (NamespaceBinding ns = prologNamespaces;
ns != builtinNamespaces;
ns = ns.getNext())
{
if (ns.getPrefix() == prefix)
{
error('e',
"duplicate declarations for the same namespace prefix '"
+prefix+"'",
"XQST0033");
break;
}
}
pushNamespace(prefix, uri);
checkAllowedNamespaceDeclaration(prefix, uri, false);
parseSeparator();
if (command == MODULE_NAMESPACE_TOKEN)
{
ModuleExp module = comp.getModule();
String className = Compilation.mangleURI(uri)
+ '.' + XQuery.makeClassName(module.getFileName());
module.setName(className);
comp.mainClass = new ClassType(className);
module.setType(comp.mainClass);
ModuleManager manager = ModuleManager.getInstance();
ModuleInfo info = manager.find(comp);
info.setNamespaceUri(uri);
module.setType(comp.mainClass);
if (uri.length() == 0)
return syntaxError("zero-length module namespace", "XQST0088");
libraryModuleNamespace = uri;
}
return QuoteExp.voidExp;
}
}
case IMPORT_SCHEMA_TOKEN:
fatal("'import schema' not implemented", "XQST0009");
case IMPORT_MODULE_TOKEN:
getRawToken();
prefix = null;
if (match("namespace"))
{
getRawToken();
if (curToken != NCNAME_TOKEN)
return syntaxError("missing namespace prefix");
prefix = new String(tokenBuffer, 0, tokenBufferLength);
getRawToken();
if (curToken != OP_EQU)
return syntaxError("missing '=' in namespace declaration");
getRawToken();
}
if (curToken != STRING_TOKEN)
return syntaxError("missing uri in namespace declaration");
if (tokenBufferLength == 0)
return syntaxError("zero-length target namespace", "XQST0088");
uri = new String(tokenBuffer, 0, tokenBufferLength).intern();
if (prefix != null)
{
checkAllowedNamespaceDeclaration(prefix, uri, false);
pushNamespace(prefix.intern(), uri);
}
getRawToken();
// Make sure we have a ModuleInfo before we call importDefinitions.
ModuleManager.getInstance().find(comp);
String at;
ModuleExp module = comp.getModule();
Vector forms = new Vector();
String packageName = Compilation.mangleURI(uri);
comp.setLine(port.getName(), startLine, startColumn);
if (match("at"))
{
for (;;)
{
getRawToken();
if (curToken != STRING_TOKEN)
return syntaxError("missing module location");
at = new String(tokenBuffer, 0, tokenBufferLength);
String className = Compilation.mangleURI(uri)
+ '.' + XQuery.makeClassName(at);
ModuleInfo info = require.lookupModuleFromSourcePath(at, module);
if (info == null)
comp.error('e', "malformed URL: "+at);
require.importDefinitions(className, info,
uri, forms, module, comp);
next = skipSpace(nesting != 0);
if (next != ',')
{
unread(next);
break;
}
}
parseSeparator();
}
else
{
ModuleManager manager = ModuleManager.getInstance();
int n = 0;
try
{
manager.loadPackageInfo(packageName);
}
catch (ClassNotFoundException ex)
{
// Do nothing. If there is no such module,
// that will be reported below.
}
catch (Throwable ex)
{
error('e', "error loading map for "+uri+" - "+ex);
}
for (ModuleInfo info = manager.firstModule(); info != null;
info = info.nextModule())
{
if (! uri.equals(info.getNamespaceUri()))
continue;
n++;
require.importDefinitions(info.className, info, uri, forms, module, comp);
}
if (n == 0)
error('e', "no module found for "+uri);
at = null;
if (curToken != ';')
parseSeparator();
}
if (comp.pendingImports != null && comp.pendingImports.size() > 0)
{
error('e', "module import forms a cycle", "XQST0073");
}
Expression[] inits = new Expression[forms.size()];
forms.toArray(inits);
return BeginExp.canonicalize(inits);
case DEFAULT_COLLATION_TOKEN:
if (defaultCollator != null && ! interactive)
error('e', "duplicate default collation declaration", "XQST0038");
val = parseURILiteral();
if (val instanceof Expression) // an ErrorExp
return (Expression) val;
String collation = (String) val;
try
{
collation = resolveAgainstBaseUri(collation);
defaultCollator = NamedCollator.make(collation);
}
catch (Exception ex)
{
defaultCollator = NamedCollator.codepointCollation;
error('e', "unknown collation '"+collation+"'", "XQST0038");
}
parseSeparator();
return QuoteExp.voidExp;
case DEFAULT_ELEMENT_TOKEN:
case DEFAULT_FUNCTION_TOKEN:
boolean forFunctions = curToken == DEFAULT_FUNCTION_TOKEN;
prefix = forFunctions ? XQuery.DEFAULT_FUNCTION_PREFIX
: XQuery.DEFAULT_ELEMENT_PREFIX;
if (prologNamespaces.resolve(prefix, builtinNamespaces) != null)
error('e',
"duplicate default namespace declaration",
"XQST0066");
getRawToken();
if (match("namespace"))
getRawToken();
else
{
String msg = "expected 'namespace' keyword";
if (curToken != STRING_TOKEN && curToken != OP_EQU)
return declError(msg);
else
warnOldVersion(msg);
}
if (curToken == OP_EQU || curToken == COLON_EQUAL_TOKEN)
{
warnOldVersion("extra '=' in default namespace declaration");
getRawToken();
}
if (curToken != STRING_TOKEN)
return declError("missing namespace uri");
uri = new String(tokenBuffer, 0, tokenBufferLength);
if (forFunctions)
{
functionNamespacePath = new Namespace[1];
functionNamespacePath[0] = Namespace.getInstance(uri);
}
else
{
defaultElementNamespace = uri;
}
pushNamespace(prefix, uri);
checkAllowedNamespaceDeclaration(prefix, uri, false);
parseSeparator();
return QuoteExp.voidExp;
case DECLARE_BOUNDARY_SPACE_TOKEN:
getRawToken();
if (curToken == OP_EQU)
{
warnOldVersion("obsolate '=' in boundary-space declaration");
getRawToken();
}
if (boundarySpaceDeclarationSeen && ! interactive)
syntaxError("duplicate 'declare boundary-space' seen", "XQST0068");
boundarySpaceDeclarationSeen = true;
if (match("preserve"))
boundarySpacePreserve = true;
else if (match("strip"))
boundarySpacePreserve = false;
else if (match("skip"))
{
warnOldVersion("update: declare boundary-space skip -> strip");
boundarySpacePreserve = false;
}
else
return syntaxError("boundary-space declaration must be preserve or strip");
parseSeparator();
return QuoteExp.voidExp;
case DECLARE_CONSTRUCTION_TOKEN:
getRawToken();
if (constructionModeDeclarationSeen && ! interactive)
syntaxError("duplicate 'declare construction' seen", "XQST0067");
constructionModeDeclarationSeen = true;
if (match("strip"))
constructionModeStrip = true;
else if (match("preserve"))
constructionModeStrip = false;
else
return syntaxError("construction declaration must be strip or preserve");
parseSeparator();
return QuoteExp.voidExp;
case DECLARE_COPY_NAMESPACES_TOKEN:
getRawToken();
if (copyNamespacesDeclarationSeen && ! interactive)
syntaxError("duplicate 'declare copy-namespaces' seen", "XQST0055");
copyNamespacesDeclarationSeen = true;
if (match("preserve"))
copyNamespacesMode |= XMLFilter.COPY_NAMESPACES_PRESERVE;
else if (match("no-preserve"))
copyNamespacesMode &= ~XMLFilter.COPY_NAMESPACES_PRESERVE;
else
return syntaxError("expected 'preserve' or 'no-preserve' after 'declare copy-namespaces'");
getRawToken();
if (curToken != ',')
return syntaxError("missing ',' in copy-namespaces declaration");
getRawToken();
if (match("inherit"))
copyNamespacesMode |= XMLFilter.COPY_NAMESPACES_INHERIT;
else if (match("no-inherit"))
copyNamespacesMode &= ~XMLFilter.COPY_NAMESPACES_INHERIT;
else
return syntaxError("expected 'inherit' or 'no-inherit' in copy-namespaces declaration");
parseSeparator();
return QuoteExp.voidExp;
case DEFAULT_ORDER_TOKEN:
getRawToken();
boolean sawEmpty = match("empty");
if (emptyOrderDeclarationSeen && ! interactive)
syntaxError("duplicate 'declare default empty order' seen", "XQST0069");
emptyOrderDeclarationSeen = true;
if (sawEmpty)
getRawToken();
else
syntaxError("expected 'empty greatest' or 'empty least'");
if (match("greatest"))
defaultEmptyOrder = 'G';
else if (match("least"))
defaultEmptyOrder = 'L';
else
return syntaxError("expected 'empty greatest' or 'empty least'");
parseSeparator();
return QuoteExp.voidExp;
case DECLARE_OPTION_TOKEN:
getRawToken();
if (curToken != QNAME_TOKEN)
syntaxError("expected QName after 'declare option'");
else
{
String str = new String(tokenBuffer, 0, tokenBufferLength);
getRawToken();
if (curToken != STRING_TOKEN)
syntaxError("expected string literal after 'declare option <QName>'");
else
handleOption(namespaceResolve(str, false),
new String(tokenBuffer, 0, tokenBufferLength));
}
parseSeparator();
seenDeclaration = true;
return QuoteExp.voidExp;
case DECLARE_ORDERING_TOKEN:
if (orderingModeSeen && ! interactive)
syntaxError("duplicate 'declare ordering' seen", "XQST0065");
orderingModeSeen = true;
getRawToken();
if (match("ordered"))
orderingModeUnordered = false;
else if (match("unordered"))
orderingModeUnordered = true;
else
return syntaxError("ordering declaration must be ordered or unordered");
parseSeparator();
return QuoteExp.voidExp;
case XQUERY_VERSION_TOKEN:
if (parseCount != 1)
error('e', "'xquery version' does not start module");
else if (commentCount > 0)
error('w', "comments should not precede 'xquery version'");
getRawToken();
if (curToken == STRING_TOKEN)
{
String version = new String(tokenBuffer, 0, tokenBufferLength);
if (! version.equals("1.0"))
error('e', "unrecognized xquery version "+version, "XQST0031");
getRawToken();
}
else
return syntaxError("missing version string after 'xquery version'");
if (match("encoding"))
{
getRawToken();
if (curToken != STRING_TOKEN)
return syntaxError("invalid encoding specification");
else
{
String encoding = new String(tokenBuffer, 0, tokenBufferLength);
int i = tokenBufferLength;
boolean bad = i == 0;
while (--i >= 0 && ! bad)
{
ch = tokenBuffer[i];
if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
continue;
if (i == 0
|| ! ((ch >= '0' && ch <= '9')
|| ch == '.' || ch == '_' || ch == '-'))
bad = true;
}
if (bad)
error('e', "invalid encoding name syntax", "XQST0087");
// ignore encoding specification.
getRawToken();
}
}
if (curToken != ';')
syntaxError("missing ';'");
return QuoteExp.voidExp;
case DECLARE_BASE_URI_TOKEN:
if (baseURIDeclarationSeen && ! interactive)
syntaxError("duplicate 'declare base-uri' seen", "XQST0032");
baseURIDeclarationSeen = true;
val = parseURILiteral();
if (val instanceof Expression) // an ErrorExp
return (Expression) val;
parseSeparator();
setStaticBaseUri((String) val);
return QuoteExp.voidExp;
}
exp = parseExprSequence(EOF_TOKEN, true);
if (curToken == EOL_TOKEN)
unread('\n');
maybeSetLine(exp, startLine, startColumn);
return exp;
}
public void handleOption (Symbol name, String value)
{
// Nothing, for now.
}
public final static String[] axisNames = new String[COUNT_OP_AXIS];
static
{
axisNames[AXIS_ANCESTOR] = "ancestor";
axisNames[AXIS_ANCESTOR_OR_SELF] = "ancestor-or-self";
axisNames[AXIS_ATTRIBUTE] = "attribute";
axisNames[AXIS_CHILD] = "child";
axisNames[AXIS_DESCENDANT] = "descendant";
axisNames[AXIS_DESCENDANT_OR_SELF] = "descendant-or-self";
axisNames[AXIS_FOLLOWING] = "following";
axisNames[AXIS_FOLLOWING_SIBLING] = "following-sibling";
axisNames[AXIS_NAMESPACE] = "namespace";
axisNames[AXIS_PARENT] = "parent";
axisNames[AXIS_PRECEDING] = "preceding";
axisNames[AXIS_PRECEDING_SIBLING] = "preceding-sibling";
axisNames[AXIS_SELF] = "self";
}
public static Expression makeFunctionExp(String className, String name)
{
return makeFunctionExp(className,
Compilation.mangleNameIfNeeded(name),
name);
}
public static Expression makeFunctionExp(String className,
String fieldName, String name)
{
return new ReferenceExp(name,
Declaration.getDeclarationValueFromStatic
(className, fieldName, name));
}
/** Helper method for debugging. */
String tokenString()
{
switch (curToken)
{
case STRING_TOKEN:
StringBuffer sbuf = new StringBuffer();
sbuf.append('"');
for (int i = 0; i < tokenBufferLength; i++)
{
char ch = tokenBuffer[i];
if (ch == '"')
sbuf.append('"');
sbuf.append(ch);
}
sbuf.append('"');
return sbuf.toString();
case FNAME_TOKEN:
return new String(tokenBuffer, 0, tokenBufferLength) + " + '('";
case NCNAME_TOKEN:
case QNAME_TOKEN:
return new String(tokenBuffer, 0, tokenBufferLength);
case EOF_TOKEN:
return "<EOF>";
default:
if (curToken >= OP_AXIS_FIRST
&& curToken - OP_AXIS_FIRST < COUNT_OP_AXIS)
return axisNames[curToken - OP_AXIS_FIRST]+"::-axis("+curToken+")";
return Integer.toString(curToken);
}
}
public void error(char severity, String message, String code)
{
SourceMessages messages = getMessages();
SourceError err
= new SourceError(severity, port.getName(), curLine, curColumn, message);
err.code = code;
messages.error(err);
}
public void error(char severity, String message)
{
error(severity, message, null);
}
public Expression declError (String message)
throws java.io.IOException, SyntaxException
{
if (interactive)
return syntaxError(message);
error(message);
for (;;)
{
if (curToken==';' || curToken == EOF_TOKEN)
break;
getRawToken();
}
return new ErrorExp (message);
}
/**
* Handle syntax errors (at rewrite time).
* @param message an error message to print out
* @return an ErrorExp
*/
public Expression syntaxError (String message, String code)
throws java.io.IOException, SyntaxException
{
error('e', message, code);
if (interactive)
{
curToken = 0;
curValue = null;
nesting = 0;
((InPort) getPort()).readState = '\n';
for (;;)
{
int ch = read();
if (ch < 0)
break;
if (ch == '\r' || ch == '\n')
{
unread(ch);
break;
}
}
throw new SyntaxException(getMessages());
}
return new ErrorExp (message);
}
public Expression syntaxError (String message)
throws java.io.IOException, SyntaxException
{
return syntaxError(message, "XPST0003");
}
public void eofError(String msg) throws SyntaxException
{
fatal(msg, "XPST0003");
}
public void fatal(String msg, String code) throws SyntaxException
{
SourceMessages messages = getMessages();
SourceError err
= new SourceError('f', port.getName(), curLine, curColumn, msg);
err.code = code;
messages.error(err);
throw new SyntaxException(messages);
}
void warnOldVersion (String message)
{
if (warnOldVersion || comp.isPedantic())
error(comp.isPedantic() ? 'e' : 'w', message);
}
public void maybeSetLine (Expression exp, int line, int column)
{
String file = getName();
if (file != null)
{
exp.setFile(file);
exp.setLine(line, column);
}
}
public void maybeSetLine (Declaration decl, int line, int column)
{
String file = getName();
if (file != null)
{
decl.setFile(file);
decl.setLine(line, column);
}
}
}
|