From: <mk...@us...> - 2006-11-10 15:51:00
|
Revision: 1556 http://svn.sourceforge.net/vexi/?rev=1556&view=rev Author: mkpg2 Date: 2006-11-10 07:49:13 -0800 (Fri, 10 Nov 2006) Log Message: ----------- Added indent action (based on imported and modified org.ibex.js.Parser) Reorganised tests. Modified Paths: -------------- tools/trunk/org.vexi.vexidev/plugin.xml tools/trunk/org.vexi.vexidev/src/org/vexi/vexidev/debug/VexiRunnerConfig.java tools/trunk/org.vexi.vexidev/src/org/vexi/vexidev/debug/ui/LaunchPathPage.java tools/trunk/org.vexi.vexidev/src/org/vexi/vexidev/debug/ui/LaunchShortcut.java tools/trunk/org.vexi.vexidev/src/org/vexi/vexidev/debug/ui/RunActionDelegate.java tools/trunk/org.vexi.vexidev/src/org/vexi/vexidev/editor/TagRule.java tools/trunk/org.vexi.vexidev/src/org/vexi/vexidev/editor/TemplateFilePartitionScanner.java tools/trunk/org.vexi.vexidev/src/org/vexi/vexidev/editor/VexiDocumentProvider.java tools/trunk/org.vexi.vexidev/src/org/vexi/vexidev/editor/VexiEditor.java tools/trunk/org.vexi.vexidev/src/org/vexi/vexidev/editor/actions/FormatActionDelegate.java tools/trunk/org.vexi.vexidev/src/org/vexi/vexidev/editor/actions/IndentActionDelegate.java tools/trunk/org.vexi.vexidev/src/org/vexi/vexidev/ui/EditableList.java Added Paths: ----------- tools/trunk/org.vexi.vexidev/README.txt tools/trunk/org.vexi.vexidev/src/org/ibex/ tools/trunk/org.vexi.vexidev/src/org/ibex/js/ tools/trunk/org.vexi.vexidev/src/org/ibex/js/ByteCodes.java tools/trunk/org.vexi.vexidev/src/org/ibex/js/FormatParser.java tools/trunk/org.vexi.vexidev/src/org/ibex/js/Lexer_.java tools/trunk/org.vexi.vexidev/src/org/ibex/js/Tokens.java tools/trunk/org.vexi.vexidev/src/org/ibex/util/ tools/trunk/org.vexi.vexidev/src/org/ibex/util/Basket.java tools/trunk/org.vexi.vexidev/src/org/ibex/util/Queue.java tools/trunk/org.vexi.vexidev/src/org/ibex/util/SourceException.java tools/trunk/org.vexi.vexidev/src/org/ibex/util/Tree.java tools/trunk/org.vexi.vexidev/src/org/ibex/util/Vec.java tools/trunk/org.vexi.vexidev/src/org/ibex/util/XML_.java tools/trunk/org.vexi.vexidev/src/org/vexi/format/ tools/trunk/org.vexi.vexidev/src/org/vexi/format/AbstractFormatter.java tools/trunk/org.vexi.vexidev/src/org/vexi/format/IIndentList.java tools/trunk/org.vexi.vexidev/src/org/vexi/format/IIndentPhases.java tools/trunk/org.vexi.vexidev/src/org/vexi/format/Indenter.java tools/trunk/org.vexi.vexidev/src/org/vexi/format/JSIndenter.java tools/trunk/org.vexi.vexidev/src/org/vexi/format/LineTrapReader.java tools/trunk/org.vexi.vexidev/src/org/vexi/vexidev/debug/ui/LaunchShortcut2.java tools/trunk/org.vexi.vexidev/src_junit/test/ tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/ tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/EditorActionTestCase.java tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/TestEditorAction.java tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/format/ tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/format/TestFormatting.java tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/format/braces.in tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/format/braces.out tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/format/commenttag.in tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/format/commenttag.out tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/format/formatfunc.in tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/format/formatfunc.out tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/format/objectdec.in tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/format/objectdec.out tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/ tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/TestIndenting.java tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/case1.in tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/case1.out tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/case1.out_2_5 tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/case1.out_3_4 tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/case1b.in tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/case1b.out_4_5 tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/case2.in tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/case2.out tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/case3.in tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/case3.out_3_3 tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/case3.out_4_5 tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/js/ tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/js/TestJSIndenting.java tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/js/blocks.in tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/js/blocks.out tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/js/blocks.out_4_7 tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/js/blocks.out_8_8 tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/js/exp.in tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/js/exp.out tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/js/ifelse.in tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/js/ifelse.out tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/js/ifelse.out_4_6 tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/js/loop.in tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/js/loop.out tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/js/nested.in tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/js/nested.out tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/js/startindent.in tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/js/startindent.out tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/js/switch.in tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/js/switch.out tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/static.in tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/static.out_2_2 tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/static2.in tools/trunk/org.vexi.vexidev/src_junit/test/editoractions/indent/static2.out_2_2 Removed Paths: ------------- tools/trunk/org.vexi.vexidev/src/org/vexi/vexidev/debug/VexiPathBlock.java Added: tools/trunk/org.vexi.vexidev/README.txt =================================================================== --- tools/trunk/org.vexi.vexidev/README.txt (rev 0) +++ tools/trunk/org.vexi.vexidev/README.txt 2006-11-10 15:49:13 UTC (rev 1556) @@ -0,0 +1,8 @@ + +Imported Ibex code + +For the formatting it was necessary to extend/modify the ibex js parser. In the ibex directories +FormatParser is a heavily modified version of the Parser that does not output byte codes, but only +triggers events. + +Classes with names ending in _ are slightly modified (e.g. changing visibilities ... etc.) \ No newline at end of file Modified: tools/trunk/org.vexi.vexidev/plugin.xml =================================================================== --- tools/trunk/org.vexi.vexidev/plugin.xml 2006-11-10 15:41:18 UTC (rev 1555) +++ tools/trunk/org.vexi.vexidev/plugin.xml 2006-11-10 15:49:13 UTC (rev 1556) @@ -24,6 +24,7 @@ contributorClass="org.vexi.vexidev.editor.VexiEditorActionContributor" class="org.vexi.vexidev.editor.VexiEditor" id="org.vexi.vexidev.editor.VexiEditor"> + <contentTypeBinding contentTypeId="org.vexi.vexidev.contentTypeBinding1"/> </editor> </extension> <extension @@ -83,7 +84,7 @@ <page objectClass="org.eclipse.core.resources.IProject" class="org.vexi.vexidev.ui.projectprops.VexiProjectProperties" - name="Vexidev VEXIPATH" + name="Vexi" id="org.vexi.vexidev.ui.vexiProjectProperties"> <filter value="org.vexi.vexidev.vexiNature" @@ -111,18 +112,41 @@ menubarPath="org.vexi.vexidev.editor.actions.sourceMenu/editGroup" id="org.vexi.vexidev.editor.actions.FormatAction"> </action> -<!-- TODO - implement this <action class="org.vexi.vexidev.editor.actions.IndentActionDelegate" - definitionId="org.vexi.vexidev.editor.actions.IndentAction" + definitionId="org.vexi.vexidev.ui.commands.indent" id="org.vexi.vexidev.editor.actions.IndentAction" label="Indent " menubarPath="org.vexi.vexidev.editor.actions.sourceMenu/editGroup" - style="push"/>--> + style="push"/> </editorContribution> </extension> <extension + point="org.eclipse.ui.bindings"> + <key + sequence="M1+I" + contextId="org.eclipse.ui.textEditorScope" + commandId="org.vexi.vexidev.ui.commands.indent" + schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"/> + + </extension> + <extension + point="org.eclipse.ui.commands"> + <category + name="Source" + description="Source commands category" + id="org.vexi.vexidev.ui.category.source"> + </category> + <command + name="Indent" + description="Correct code indentation" + categoryId="org.vexi.vexidev.ui.category.source" + id="org.vexi.vexidev.ui.commands.indent"> + </command> + </extension> + + <extension point="org.eclipse.ui.popupMenus"> <objectContribution adaptable="true" @@ -143,7 +167,33 @@ label="Run" menubarPath="org.vexi.vexidev.launchMenu/group1" tooltip="Run as main template file. "/> + <action + class="org.vexi.vexidev.debug.ui.RunActionDelegate" + enablesFor="1" + icon="icons/vexi.png" + id="org.vexi.vexidev.runAction2" + label="Run (conf)" + menubarPath="org.vexi.vexidev.launchMenu/group1" + tooltip="Run as main template file. Configure first."/> </objectContribution> </extension> + <extension + point="org.eclipse.ui.popupMenus"> + <viewerContribution + id="org.vexi.vexidev.editorContribution" + targetID="org.vexi.vexidev.editor.VexiEditor.EditorContext"> + <menu + id="org.vexi.vexidev.editorPopupMenu" + label="Source" + path="additions"> + <groupMarker name="group1"/> + </menu> + <action + class="org.vexi.vexidev.editor.actions.IndentActionDelegate" + id="org.vexi.vexidev.indentAction" + label="Indent" + menubarPath="org.vexi.vexidev.editorPopupMenu/group1"/> + </viewerContribution> + </extension> </plugin> Added: tools/trunk/org.vexi.vexidev/src/org/ibex/js/ByteCodes.java =================================================================== --- tools/trunk/org.vexi.vexidev/src/org/ibex/js/ByteCodes.java (rev 0) +++ tools/trunk/org.vexi.vexidev/src/org/ibex/js/ByteCodes.java 2006-11-10 15:49:13 UTC (rev 1556) @@ -0,0 +1,103 @@ +// Copyright 2000-2005 the Contributors, as shown in the revision logs. +// Licensed under the Apache Public Source License 2.0 ("the License"). +// You may not use this file except in compliance with the License. + +package org.ibex.js; + +/** + * Constants for the various JavaScript ByteCode operations. + * + * Each instruction is an opcode and an optional literal literal; + * some Tokens are also valid; see Tokens.java + */ +interface ByteCodes { + + /** push the literal onto the stack */ + public static final byte LITERAL = -2; + + /** push a new array onto the stack with length equal to the literal */ + public static final byte ARRAY = -3; + + /** push an empty object onto the stack */ + public static final byte OBJECT = -4; + + /** create a new instance; literal is a reference to the corresponding ForthBlock */ + public static final byte NEWFUNCTION = -5; + + /** if given a non-null argument declare its argument in the current scope and push + it to the stack, else, declares the element on the top of the stack and leaves it + there */ + //public static final byte DECLARE = -6; + + /** push a reference to the current scope onto the stack */ + public static final byte GLOBALSCOPE = -7; + + /** if given a null literal pop two elements off the stack; push stack[-1].get(stack[top]) + else pop one element off the stack, push stack[top].get(literal) */ + public static final byte GET = -8; + + /** push stack[-1].get(stack[top]) */ + public static final byte GET_PRESERVE = -9; + + /** pop two elements off the stack; stack[-2].put(stack[-1], stack[top]); push stack[top] */ + public static final byte PUT = -10; + + /** literal is a relative address; pop stacktop and jump if the value is true */ + public static final byte JT = -11; + + /** literal is a relative address; pop stacktop and jump if the value is false */ + public static final byte JF = -12; + + /** literal is a relative address; jump to it */ + public static final byte JMP = -13; + + /** discard the top stack element */ + static public final byte POP = -14; + + /** pop element; call stack[top](stack[-n], stack[-n+1]...) where n is the number of args to the function */ + public static final byte CALL = -15; + + /** pop an element; push a JS.JSArray containing the keys of the popped element */ + public static final byte PUSHKEYS = -16; + + /** push the top element down so that (arg) elements are on top of it; all other elements retain ordering */ + public static final byte SWAP = -17; + + /** execute the bytecode block pointed to by the literal in a fresh scope with parentScope==THIS */ + public static final byte NEWSCOPE = -18; + + /** execute the bytecode block pointed to by the literal in a fresh scope with parentScope==THIS */ + public static final byte OLDSCOPE = -19; + + /** push a copy of the top stack element */ + public static final byte DUP = -20; + + /** a NOP; confers a label upon the following instruction */ + public static final byte LABEL = -21; + + /** execute the ForthBlock pointed to by the literal until BREAK encountered; push TRUE onto the stack for the first iteration + * and FALSE for all subsequent iterations */ + public static final byte LOOP = -22; + + /** similar effect a a GET followed by a CALL */ + public static final byte CALLMETHOD = -23; + + /** finish a finally block and carry out whatever instruction initiated the finally block */ + public static final byte FINALLY_DONE = -24; + + /** finish a finally block and carry out whatever instruction initiated the finally block */ + public static final byte MAKE_GRAMMAR = -25; + + // FEATURE: Document these and NEWSCOPE/OLDSCOPE/TOPSCOPE changes + /** takes the top value from the stack and puts it into the scope, using its arg (JSInt) as the + * location in the scope*/ + public static final byte SCOPEGET = -26; + public static final byte SCOPEPUT = -27; + + public static final String[] bytecodeToString = new String[] { + "", "", "LITERAL", "ARRAY", "OBJECT", "NEWFUNCTION", "DECLARE", "GLOBALSCOPE", + "GET", "GET_PRESERVE", "PUT", "JT", "JF", "JMP", "POP", "CALL", "PUSHKEYS", + "SWAP", "NEWSCOPE", "OLDSCOPE", "DUP", "LABEL", "LOOP", "CALLMETHOD", + "FINALLY_DONE", "MAKE_GRAMMAR", "SCOPEGET", "SCOPEPUT" + }; +} Added: tools/trunk/org.vexi.vexidev/src/org/ibex/js/FormatParser.java =================================================================== --- tools/trunk/org.vexi.vexidev/src/org/ibex/js/FormatParser.java (rev 0) +++ tools/trunk/org.vexi.vexidev/src/org/ibex/js/FormatParser.java 2006-11-10 15:49:13 UTC (rev 1556) @@ -0,0 +1,615 @@ +// Copyright 2000-2005 the Contributors, as shown in the revision logs. +// Licensed under the Apache Public Source License 2.0 ("the License"). +// You may not use this file except in compliance with the License. + +package org.ibex.js; + +import java.io.IOException; +import java.io.Reader; + +import org.ibex.util.Basket; +import org.ibex.util.SourceException; + +/** + * A modified org.ibex.js.Parser for formatting. + */ +abstract public class FormatParser extends Lexer_ implements ByteCodes{ + + // Constructors ////////////////////////////////////////////////////// + + public FormatParser(Reader r) throws IOException { super(r, "", 0);} + + // Statics //////////////////////////////////////////////////////////// + + static byte[] precedence = new byte[MAX_TOKEN + 1]; + static boolean[] isRightAssociative = new boolean[MAX_TOKEN + 1]; + // Use this as the precedence when we want anything up to the comma + private final static int NO_COMMA = 2; + static { + isRightAssociative[ASSIGN] = + isRightAssociative[ASSIGN_BITOR] = + isRightAssociative[ASSIGN_BITXOR] = + isRightAssociative[ASSIGN_BITAND] = + isRightAssociative[ASSIGN_LSH] = + isRightAssociative[ASSIGN_RSH] = + isRightAssociative[ASSIGN_URSH] = + isRightAssociative[ASSIGN_ADD] = + isRightAssociative[ASSIGN_SUB] = + isRightAssociative[ASSIGN_MUL] = + isRightAssociative[ASSIGN_DIV] = + isRightAssociative[ASSIGN_MOD] = + isRightAssociative[ADD_TRAP] = + isRightAssociative[DEL_TRAP] = + true; + + precedence[COMMA] = 1; + // 2 is intentionally left unassigned. we use minPrecedence==2 for comma separated lists + precedence[ASSIGN] = + precedence[ASSIGN_BITOR] = + precedence[ASSIGN_BITXOR] = + precedence[ASSIGN_BITAND] = + precedence[ASSIGN_LSH] = + precedence[ASSIGN_RSH] = + precedence[ASSIGN_URSH] = + precedence[ASSIGN_ADD] = + precedence[ASSIGN_SUB] = + precedence[ASSIGN_MUL] = + precedence[ASSIGN_DIV] = + precedence[ADD_TRAP] = + precedence[DEL_TRAP] = + precedence[ASSIGN_MOD] = 3; + precedence[HOOK] = 4; + precedence[OR] = 5; + precedence[AND] = 6; + precedence[BITOR] = 7; + precedence[BITXOR] = 8; + precedence[BITAND] = 9; + precedence[EQ] = precedence[NE] = precedence[SHEQ] = precedence[SHNE] = 10; + precedence[LT] = precedence[LE] = precedence[GT] = precedence[GE] = 11; + precedence[LSH] = precedence[RSH] = precedence[URSH] = 12; + precedence[ADD] = precedence[SUB] = 12; + precedence[MUL] = precedence[DIV] = precedence[MOD] = 13; + precedence[BITNOT] = precedence[BANG] = precedence[TYPEOF] = 14; + precedence[DOT] = precedence[LB] = precedence[LP] = precedence[INC] = precedence[DEC] = 15; + } + + + // Parsing Logic ///////////////////////////////////////////////////////// + protected StringBuffer out; + + + + + /** gets a token and throws an exception if it is not <tt>code</tt> */ + private void consume(int code) throws IOException { + if (getToken() != code) { + if(code == NAME) switch(op) { + case RETURN: case TYPEOF: case BREAK: case CONTINUE: case TRY: case THROW: + case ASSERT: case NULL: case TRUE: case FALSE: case IN: case IF: case ELSE: + case SWITCH: case CASE: case DEFAULT: case WHILE: case VAR: case WITH: + case CATCH: case FINALLY: + throw pe("Bad variable name; '" + codeToString[op].toLowerCase() + "' is a javascript keyword"); + } + throw pe("expected " + codeToString[code] + ", got " + (op == -1 ? "EOF" : codeToString[op])); + } + } + + /** + * Parse the largest possible expression containing no operators + * of precedence below <tt>minPrecedence</tt> and append the + * bytecodes for that expression to <tt>appendTo</tt>; the + * appended bytecodes MUST grow the stack by exactly one element. + */ + protected void startExpr(int minPrecedence) throws IOException { + int saveParserLine = parserLine; + _startExpr(minPrecedence); + parserLine = saveParserLine; + } + private void _startExpr( int minPrecedence) throws IOException { + int tok = getToken(); + + switch (tok) { + case -1: throw pe("expected expression"); + + // all of these simply push values onto the stack + case NUMBER: + case STRING: + case NULL: + case TRUE: break; + + // (.foo) syntax + case DOT: { + consume(NAME); + continueExprAfterAssignable(minPrecedence); + break; + } + + case LB: { + if (peekToken() != RB) + while(true) { // iterate over the initialization values + if (peekToken() == COMMA || peekToken() == RB){} + else + startExpr(NO_COMMA); // push the value onto the stack + if (peekToken() == RB) break; + consume(COMMA); + } + consume(RB); + break; + } + case SUB: case ADD: { + if(peekToken() == NUMBER) { // literal + consume(NUMBER); + } else { // unary +/- operator + // BITNOT has the same precedence as the unary +/- operators + startExpr(precedence[BITNOT]); + } + break; + } + case LP: { // grouping (not calling) + startExpr(-1); + consume(RP); + break; + } + case INC: case DEC: { // prefix (not postfix) + startExpr( precedence[tok]); + break; + } + case BANG: case BITNOT: case TYPEOF: { + startExpr( precedence[tok]); + break; + } + case LC: { // object constructor + if (peekToken() != RC) + while(true) { + if (peekToken() != NAME && peekToken() != STRING) + throw pe("expected NAME or STRING"); + getToken(); + consume(COLON); + startExpr(NO_COMMA); // grab the value + if (peekToken() == RC) break; + consume(COMMA); + if (peekToken() == RC) break; // we permit {,,} -- I'm not sure if ECMA does + } + consume(RC); + break; + } + case NAME: { + continueExprAfterAssignable(minPrecedence); + break; + } + case CASCADE: { + if(peekToken() == ASSIGN) { + consume(ASSIGN); + startExpr( precedence[ASSIGN]); + } else { + } + break; + } + + case FUNCTION: { + consume(LP); + int numArgs = 0; + while(peekToken() != RP) { // run through the list of argument names + numArgs++; + if (peekToken() == NAME) { + consume(NAME); // a named argument + } + if (peekToken() == RP) break; + consume(COMMA); + } + consume(RP); + + if(peekToken() != LC) + throw pe("JSFunctions must have a block surrounded by curly brackets"); + + parseBlock(null); // the function body + break; + } + default: throw pe("expected expression, found " + codeToString[tok] + ", which cannot start an expression"); + } + + // attempt to continue the expression + continueExpr(minPrecedence); + } + + /** + * Assuming that a complete assignable (lvalue) has just been + * parsed and the object and key are on the stack, + * <tt>continueExprAfterAssignable</tt> will attempt to parse an + * expression that modifies the assignable. This method always + * decreases the stack depth by exactly one element. + */ + protected void continueExprAfterAssignable(int minPrecedence) throws IOException { + int saveParserLine = parserLine; + _continueExprAfterAssignable(minPrecedence); + parserLine = saveParserLine; + } + private void _continueExprAfterAssignable(int minPrecedence) throws IOException { + int tok = getToken(); + if (minPrecedence != -1 && (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok]))) + // force the default case + tok = -1; + switch(tok) { + case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH: case ASSIGN_RSH: case ASSIGN_URSH: + case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD: case ASSIGN_ADD: case ASSIGN_SUB: case ADD_TRAP: case DEL_TRAP: { + startExpr(precedence[tok]); + break; + } + case INC: case DEC: { // postfix + break; + } + case ASSIGN: { + startExpr(precedence[tok]); + break; + } + case LP: { + int n = parseArgs(); + break; + } + default: { + pushBackToken(); + return; + } + } + } + + + /** + * Assuming that a complete expression has just been parsed, + * <tt>continueExpr</tt> will attempt to extend this expression by + * parsing additional tokens and appending additional bytecodes. + * + * No operators with precedence less than <tt>minPrecedence</tt> + * will be parsed. + * + * If any bytecodes are appended, they will not alter the stack + * depth. + */ + private void continueExpr(int minPrecedence) throws IOException { + int saveParserLine = parserLine; + _continueExpr(minPrecedence); + parserLine = saveParserLine; + } + private void _continueExpr(int minPrecedence) throws IOException { + int tok = getToken(); + if (tok == -1) return; + if (minPrecedence != -1 && (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok]))) { + pushBackToken(); + return; + } + + switch (tok) { + case LP: { // invocation (not grouping) + int n = parseArgs(); + break; + } + case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH: + case RSH: case URSH: case MUL: case DIV: case MOD: + case GT: case GE: case EQ: case NE: case LT: case LE: case SUB: { + startExpr(precedence[tok]); + break; + } + case ADD: { + int count=1; + int nextTok; + do { + startExpr(precedence[tok]); + count++; + nextTok = getToken(); + } while(nextTok == tok); + pushBackToken(); + break; + } + case OR: case AND: { + startExpr(precedence[tok]); // otherwise check the second value + break; + } + case DOT: { + // support foo..bar syntax for foo[""].bar + if (peekToken() == DOT) { + string = ""; + } else { + consume(NAME); + } + continueExprAfterAssignable(minPrecedence); + break; + } + case LB: { // subscripting (not array constructor) + startExpr(-1); + consume(RB); + continueExprAfterAssignable(minPrecedence); + break; + } + case HOOK: { + startExpr(minPrecedence); // write the if-true expression + consume(COLON); + startExpr(minPrecedence); // write the if-false expression + break; + } + case COMMA: { + // pop the result of the previous expression, it is ignored + startExpr(-1); + break; + } + default: { + pushBackToken(); + return; + } + } + + continueExpr(minPrecedence); // try to continue the expression + } + + // parse a set of comma separated function arguments, assume LP has already been consumed + private int parseArgs() throws IOException { + int i = 0; + while(peekToken() != RP) { + i++; + if (peekToken() != COMMA) { + startExpr(NO_COMMA); + if (peekToken() == RP) break; + } + consume(COMMA); + } + consume(RP); + return i; + } + + /** Parse a block of statements which must be surrounded by LC..RC. */ + void parseBlock() throws IOException { parseBlock(null); } + protected void parseBlock(String label) throws IOException { + int saveParserLine = parserLine; + _parseBlock(label); + parserLine = saveParserLine; + } + void _parseBlock(String label) throws IOException { + if (peekToken() == -1) return; + else if (peekToken() != LC) parsePseudoBlock(); + else { + consume(LC); + startBlock(); + while(peekToken() != RC && peekToken() != -1) parseStatement(null); + endBlock(); + consume(RC); + } + } + + + /** Parse a single statement, consuming the RC or SEMI which terminates it. */ + protected void parseStatement(String label) throws IOException { + int saveParserLine = parserLine; + _parseStatement(label); + parserLine = saveParserLine; + } + void _parseStatement(String label) throws IOException { + int tok = peekToken(); + if (tok == -1) return; + switch(tok = getToken()) { + + case THROW: case ASSERT: case RETURN: { + if (tok == RETURN && peekToken() == SEMI){} + else + startExpr(-1); + consume(SEMI); + break; + } + case BREAK: case CONTINUE: { + if (peekToken() == NAME) consume(NAME); + consume(SEMI); + break; + } + case VAR: { + while(true) { + consume(NAME); + if (peekToken() == ASSIGN) { // if there is an '=' after the variable name + consume(ASSIGN); + startExpr( NO_COMMA); + } + if (peekToken() != COMMA) break; + consume(COMMA); + } + if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI); + break; + } + case IF: { + consume(LP); + startExpr(-1); + consume(RP); + + parsePseudoBlock(); + + if (peekToken() == ELSE) { + consume(ELSE); + parsePseudoBlock(); + } + break; + } + case WHILE: { + consume(LP); + startLoopStuff(); + startExpr(-1); + endLoopStuff(); + consume(RP); + parsePseudoBlock(); + break; + } + case SWITCH: { + consume(LP); + startExpr(-1); + consume(RP); + consume(LC); + OUTER: for(;;) { + tok = getToken(); + switch(tok) { + case CASE: + startExpr(-1); + consume(COLON); + while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) parsePseudoBlock(); + break; + case DEFAULT: + consume(COLON); + while(peekToken() != RC) parsePseudoBlock(); + consume(RC); + break OUTER; + case RC: + break OUTER; + default: + throw pe("expected CASE, DEFAULT, or RC; got " + codeToString[peekToken()]); + } + } + break; + } + + case DO: { + parsePseudoBlock(); + consume(WHILE); + consume(LP); + startLoopStuff(); + startExpr(-1); + endLoopStuff(); + consume(RP); + consume(SEMI); + break; + } + + case TRY: { + // parse the expression to be TRYed + parseStatement(null); + if (peekToken() != CATCH && peekToken() != FINALLY) + throw pe("try without catch or finally"); + + int catchJMPDistance = -1; + if (peekToken() == CATCH) { + Basket.List catchEnds = new Basket.Array(); + boolean catchAll = false; + + + while(peekToken() == CATCH && !catchAll) { + String exceptionVar; + getToken(); + consume(LP); + consume(NAME); + if (peekToken() != RP) { + // extended Ibex catch block: catch(e faultCode "foo.bar.baz") + consume(NAME); + if (peekToken() == STRING) { + consume(STRING); + } else { + consume(NUMBER); + } + } else { + catchAll = true; + } + consume(RP); + parseBlock(null); + } + } + + int finallyJMPDistance = -1; + if (peekToken() == FINALLY) { + consume(FINALLY); + parseStatement( null); + } + break; + } + + case FOR: { + consume(LP); + startLoopStuff(); + tok = getToken(); + boolean hadVar = false; // if it's a for..in, we ignore the VAR + if (tok == VAR) { hadVar = true; tok = getToken(); } + String varName = string; + boolean forIn = peekToken() == IN; // determine if this is a for..in loop or not + pushBackToken(tok, varName); + + if (forIn) { + consume(NAME); + consume(IN); + startExpr(-1); + endLoopStuff(); + consume(RP); + parsePseudoBlock(); + } else { + if (hadVar) pushBackToken(VAR, null); // yeah, this actually matters + parseStatement(null); // initializer + if (peekToken() != SEMI) + startExpr(-1); + consume(SEMI); + if (peekToken() != RP) { // do the increment thing + startExpr(-1); + } + endLoopStuff(); + consume(RP); + parsePseudoBlock(); + } + break; + } + + case NAME: { // either a label or an identifier; this is the one place we're not LL(1) + String possiblyTheLabel = string; + if (peekToken() == COLON) { // label + consume(COLON); + parseStatement(possiblyTheLabel); + break; + } else { // expression + pushBackToken(NAME, possiblyTheLabel); + startExpr(-1); + if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI); + break; + } + } + + case SEMI: return; // yep, the null statement is valid + + case LC: { // blocks are statements too + pushBackToken(); + parseBlock(label); + break; + } + + default: { // hope that it's an expression + pushBackToken(); + startExpr(-1); + if ((mostRecentlyReadToken != RC || peekToken() == SEMI) && peekToken() != -1 && mostRecentlyReadToken != SEMI) consume(SEMI); + break; + } + } + } + + + // ParserException ////////////////////////////////////////////////////////////////////// + private IOException pe(String s) { return new ParserException(s, sourceName, line); } + + static class ParserException extends SourceException{ + + public ParserException(String message, String sourceName, int line) { + super(message, sourceName, line); + } + public String getMessageSig() { + return "[Parsing Error]"; + } + } + + + protected void parsePseudoBlock() throws IOException{ + if(peekToken()==LC) + parseBlock(null); + else{ + startBlock(); + parseStatement(null); + endBlock(); + } + } + + ///////////// + // HOOKS + ///////////// + protected void startBlock() throws IOException {}; + protected void endBlock() throws IOException {}; + protected void startLoopStuff() throws IOException {}; + protected void endLoopStuff() throws IOException {}; + + +} + Added: tools/trunk/org.vexi.vexidev/src/org/ibex/js/Lexer_.java =================================================================== --- tools/trunk/org.vexi.vexidev/src/org/ibex/js/Lexer_.java (rev 0) +++ tools/trunk/org.vexi.vexidev/src/org/ibex/js/Lexer_.java 2006-11-10 15:49:13 UTC (rev 1556) @@ -0,0 +1,415 @@ +// Derived from org.mozilla.javascript.TokenStream [NPL] + +/** + * The contents of this file are subject to the Netscape Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. + * + * Contributor(s): Roger Lawrence, Mike McCabe + */ + +package org.ibex.js; +import java.io.*; + +import org.ibex.util.SourceException; + +/** Lexes a stream of characters into a stream of Tokens */ +//MODIFIED for formatting purposes +class Lexer_ implements Tokens { + + /** for debugging */ + public static void main(String[] s) throws IOException { + Lexer_ l = new Lexer_(new InputStreamReader(System.in), "stdin", 0); + int tok = 0; + while((tok = l.getToken()) != -1) System.out.println(codeToString[tok]); + } + + /** the token that was just parsed */ + protected int op; + + /** the most recently parsed token, <i>regardless of pushbacks</i> */ + protected int mostRecentlyReadToken; + + /** if the token just parsed was a NUMBER, this is the numeric value */ + protected Number number = null; + + /** if the token just parsed was a NAME or STRING, this is the string value */ + protected String string = null; + + /** the line number of the most recently <i>lexed</i> token */ + protected int line = 0; + + /** the line number of the most recently <i>parsed</i> token */ + protected int parserLine = 0; + + /** the column number of the current token */ + protected int col = 0; + + /** the name of the source code file being lexed */ + protected String sourceName; + + private SmartReader in; + public Lexer_(Reader r, String sourceName, int line) throws IOException { + this.sourceName = sourceName; + this.line = line; + this.parserLine = line; + in = new SmartReader(r); + } + + + // Predicates /////////////////////////////////////////////////////////////////////// + + private static boolean isAlpha(int c) { return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')); } + private static boolean isDigit(int c) { return (c >= '0' && c <= '9'); } + private static int xDigitToInt(int c) { + if ('0' <= c && c <= '9') return c - '0'; + else if ('a' <= c && c <= 'f') return c - ('a' - 10); + else if ('A' <= c && c <= 'F') return c - ('A' - 10); + else return -1; + } + + + // Token Subtype Handlers ///////////////////////////////////////////////////////// + + private int getKeyword(String name) throws IOException { +final String ccSwitch0 = name; SUCCESS:do { switch(ccSwitch0.length()) { +case 2: { switch(ccSwitch0.charAt(0)) { case 'd': if ("do".equals(ccSwitch0)) { if (true) do { return DO; + } while(false); break SUCCESS; } break; case 'g': if ("gt".equals(ccSwitch0)) { if (true) do { return GT; + } while(false); break SUCCESS; } break; case 'i': { switch(ccSwitch0.charAt(1)) { case 'f': if ("if".equals(ccSwitch0)) { if (true) do { return IF; + } while(false); break SUCCESS; } break; case 'n': if ("in".equals(ccSwitch0)) { if (true) do { return IN; + } while(false); break SUCCESS; } break; } break; } case 'l': if ("lt".equals(ccSwitch0)) { if (true) do { return LT; + } while(false); break SUCCESS; } break; case 'o': if ("or".equals(ccSwitch0)) { if (true) do { return OR; + } while(false); break SUCCESS; } break; }; break; } case 3: { switch(ccSwitch0.charAt(0)) { case 'a': if ("and".equals(ccSwitch0)) { if (true) do { return AND; + } while(false); break SUCCESS; } break; case 'f': if ("for".equals(ccSwitch0)) { if (true) do { return FOR; + } while(false); break SUCCESS; } break; case 'i': if ("int".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; case 'n': if ("new".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; case 't': if ("try".equals(ccSwitch0)) { if (true) do { return TRY; + } while(false); break SUCCESS; } break; case 'v': if ("var".equals(ccSwitch0)) { if (true) do { return VAR; + } while(false); break SUCCESS; } break; }; break; } case 4: { switch(ccSwitch0.charAt(0)) { case 'b': if ("byte".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; case 'c': { switch(ccSwitch0.charAt(1)) { case 'a': if ("case".equals(ccSwitch0)) { if (true) do { return CASE; + } while(false); break SUCCESS; } break; case 'h': if ("char".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; } break; } case 'e': { switch(ccSwitch0.charAt(1)) { case 'l': if ("else".equals(ccSwitch0)) { if (true) do { return ELSE; + } while(false); break SUCCESS; } break; case 'n': if ("enum".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; } break; } case 'g': if ("goto".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; case 'l': if ("long".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; case 'n': if ("null".equals(ccSwitch0)) { if (true) do { return NULL; + } while(false); break SUCCESS; } break; case 't': if ("true".equals(ccSwitch0)) { if (true) do { return TRUE; + } while(false); break SUCCESS; } break; case 'v': if ("void".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; case 'w': if ("with".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; }; break; } case 5: { switch(ccSwitch0.charAt(0)) { case 'b': if ("break".equals(ccSwitch0)) { if (true) do { return BREAK; + } while(false); break SUCCESS; } break; case 'c': { switch(ccSwitch0.charAt(1)) { case 'a': if ("catch".equals(ccSwitch0)) { if (true) do { return CATCH; + } while(false); break SUCCESS; } break; case 'l': if ("class".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; case 'o': if ("const".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; } break; } case 'f': { switch(ccSwitch0.charAt(1)) { case 'a': if ("false".equals(ccSwitch0)) { if (true) do { return FALSE; + } while(false); break SUCCESS; } break; case 'i': if ("final".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; } break; } case 's': if ("super".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; case 't': if ("throw".equals(ccSwitch0)) { if (true) do { return THROW; + } while(false); break SUCCESS; } break; case 'w': if ("while".equals(ccSwitch0)) { if (true) do { return WHILE; + } while(false); break SUCCESS; } break; }; break; } case 6: { switch(ccSwitch0.charAt(0)) { case 'a': if ("assert".equals(ccSwitch0)) { if (true) do { return ASSERT; + } while(false); break SUCCESS; } break; case 'd': { switch(ccSwitch0.charAt(1)) { case 'e': if ("delete".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; case 'o': if ("double".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; } break; } case 'p': if ("public".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; case 'r': if ("return".equals(ccSwitch0)) { if (true) do { return RETURN; + } while(false); break SUCCESS; } break; case 's': if ("switch".equals(ccSwitch0)) { if (true) do { return SWITCH; + } while(false); break SUCCESS; } break; case 't': { switch(ccSwitch0.charAt(1)) { case 'h': if ("throws".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; case 'y': if ("typeof".equals(ccSwitch0)) { if (true) do { return TYPEOF; + } while(false); break SUCCESS; } break; } break; } }; break; } case 7: { switch(ccSwitch0.charAt(0)) { case 'b': if ("boolean".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; case 'c': if ("cascade".equals(ccSwitch0)) { if (true) do { return CASCADE; + } while(false); break SUCCESS; } break; case 'd': if ("default".equals(ccSwitch0)) { if (true) do { return DEFAULT; + } while(false); break SUCCESS; } break; case 'e': if ("extends".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; case 'f': if ("finally".equals(ccSwitch0)) { if (true) do { return FINALLY; + } while(false); break SUCCESS; } break; case 'p': { switch(ccSwitch0.charAt(1)) { case 'a': if ("package".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; case 'r': if ("private".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; } break; } }; break; } case 8: { switch(ccSwitch0.charAt(0)) { case 'a': if ("abstract".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; case 'c': if ("continue".equals(ccSwitch0)) { if (true) do { return CONTINUE; + } while(false); break SUCCESS; } break; case 'd': if ("debugger".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; case 'f': if ("function".equals(ccSwitch0)) { if (true) do { return FUNCTION; + } while(false); break SUCCESS; } break; case 'v': if ("volatile".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; }; break; } case 9: { switch(ccSwitch0.charAt(0)) { case 'i': if ("interface".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; case 'p': if ("protected".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; case 't': if ("transient".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; }; break; } case 10: { switch(ccSwitch0.charAt(0)) { case 'i': { switch(ccSwitch0.charAt(1)) { case 'm': if ("implements".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; case 'n': if ("instanceof".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; } break; } }; break; } case 12: { switch(ccSwitch0.charAt(0)) { case 's': if ("synchronized".equals(ccSwitch0)) { if (true) do { return RESERVED; + } while(false); break SUCCESS; } break; }; break; } } /* switch */ } while(false); /* OUTER */ + return -1; + } + + private int getIdentifier(int c) throws IOException { + in.startString(); + while (Character.isJavaIdentifierPart((char)(c = in.read()))); + in.unread(); + String str = in.getString(); + int result = getKeyword(str); + if (result == RESERVED) throw new LexerException("The reserved word \"" + str + "\" is not permitted in Ibex scripts"); + if (result != -1) return result; + this.string = str.intern(); + return NAME; + } + + private int getNumber(int c) throws IOException { + int base = 10; + in.startString(); + double dval = Double.NaN; + long longval = 0; + boolean isInteger = true; + + // figure out what base we're using + if (c == '0') { + if (Character.toLowerCase((char)(c = in.read())) == 'x') { base = 16; in.startString(); } + else if (isDigit(c)) base = 8; + } + + while (0 <= xDigitToInt(c) && !(base < 16 && isAlpha(c))) c = in.read(); + if (base == 10 && (c == '.' || c == 'e' || c == 'E')) { + isInteger = false; + if (c == '.') do { c = in.read(); } while (isDigit(c)); + if (c == 'e' || c == 'E') { + c = in.read(); + if (c == '+' || c == '-') c = in.read(); + if (!isDigit(c)) throw new LexerException("float listeral did not have an exponent value"); + do { c = in.read(); } while (isDigit(c)); + } + } + in.unread(); + + String numString = in.getString(); + if (base == 10 && !isInteger) { + try { dval = (Double.valueOf(numString)).doubleValue(); } + catch (NumberFormatException ex) { throw new LexerException("invalid numeric literal: \"" + numString + "\""); } + } else { + if (isInteger) { + longval = Long.parseLong(numString, base); + dval = (double)longval; + } else { + dval = Double.parseDouble(numString); + longval = (long) dval; + if (longval == dval) isInteger = true; + } + } + + if (!isInteger) this.number = new Double(dval); + else if(longval >= Integer.MIN_VALUE && longval <= Integer.MAX_VALUE) this.number = new Integer((int)longval); + else this.number = new Long(longval); + return NUMBER; + } + + private int getString(int c) throws IOException { + StringBuffer stringBuf = null; + int quoteChar = c; + c = in.read(); + in.startString(); // start after the first " + while(c != quoteChar) { + if (c == '\n' || c == -1) throw new LexerException("unterminated string literal"); + if (c == '\\') { + if (stringBuf == null) { + in.unread(); // Don't include the backslash + stringBuf = new StringBuffer(in.getString()); + in.read(); + } + switch (c = in.read()) { + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\u000B'; break; + case '\\': c = '\\'; break; + case 'u': { + int v = 0; + for(int i=0; i<4; i++) { + int ci = in.read(); + if (!((ci >= '0' && ci <= '9') || (ci >= 'a' && ci <= 'f') || (ci >= 'A' && ci <= 'F'))) + throw new LexerException("illegal character '" + ((char)c) + "' in \\u unicode escape sequence"); + v = (v << 8) | Integer.parseInt(ci + "", 16); + } + c = (char)v; + break; + } + default: + // just use the character that was escaped + break; + } + } + if (stringBuf != null) stringBuf.append((char) c); + c = in.read(); + } + if (stringBuf != null) this.string = stringBuf.toString().intern(); + else { + in.unread(); // miss the trailing " + this.string = in.getString().intern(); + in.read(); + } + return STRING; + } + + private int _getToken() throws IOException { + int c; + do { c = in.read(); } while (c == '\u0020' || c == '\u0009' || c == '\u000C' || c == '\u000B' || c == '\n' ); + if (c == -1) return -1; + if (c == '\\' || Character.isJavaIdentifierStart((char)c)) return getIdentifier(c); + if (isDigit(c) || (c == '.' && isDigit(in.peek()))) return getNumber(c); + if (c == '"' || c == '\'') return getString(c); + switch (c) { + case ';': return SEMI; + case '[': return LB; + case ']': return RB; + case '{': return LC; + case '}': return RC; + case '(': return LP; + case ')': return RP; + case ',': return COMMA; + case '?': return HOOK; + case ':': return !in.match(':') ? COLON : in.match('=') ? GRAMMAR : le(":: is not a valid token"); + case '.': return DOT; + case '|': return in.match('|') ? OR : (in.match('=') ? ASSIGN_BITOR : BITOR); + case '^': return in.match('=') ? ASSIGN_BITXOR : BITXOR; + case '&': return in.match('&') ? AND : in.match('=') ? ASSIGN_BITAND : BITAND; + case '=': return !in.match('=') ? ASSIGN : in.match('=') ? SHEQ : EQ; + case '!': return !in.match('=') ? BANG : in.match('=') ? SHNE : NE; + case '%': return in.match('=') ? ASSIGN_MOD : MOD; + case '~': return BITNOT; + case '+': return in.match('=') ? ASSIGN_ADD : in.match('+') ? (in.match('=') ? ADD_TRAP : INC) : ADD; + case '-': return in.match('=') ? ASSIGN_SUB: in.match('-') ? (in.match('=') ? DEL_TRAP : DEC) : SUB; + case '*': return in.match('=') ? ASSIGN_MUL : MUL; + case '<': return !in.match('<') ? (in.match('=') ? LE : LT) : in.match('=') ? ASSIGN_LSH : LSH; + case '>': return !in.match('>') ? (in.match('=') ? GE : GT) : + in.match('>') ? (in.match('=') ? ASSIGN_URSH : URSH) : (in.match('=') ? ASSIGN_RSH : RSH); + case '/': + if (in.match('=')) return ASSIGN_DIV; + if (in.match('/')) { while ((c = in.read()) != -1 && c != '\n'); in.unread(); return getToken(); } + if (!in.match('*')) return DIV; + while ((c = in.read()) != -1 && !(c == '*' && in.match('/'))) { + if (c == '\n' || c != '/' || !in.match('*')) continue; + if (in.match('/')) return getToken(); + throw new LexerException("nested comments are not permitted"); + } + if (c == -1) throw new LexerException("unterminated comment"); + return getToken(); // `goto retry' + default: throw new LexerException("illegal character: \'" + ((char)c) + "\'"); + } + } + + private int le(String s) throws LexerException { if (true) throw new LexerException(s); return 0; } + + // SmartReader //////////////////////////////////////////////////////////////// + + /** a Reader that tracks line numbers and can push back tokens */ + private class SmartReader { + PushbackReader reader = null; + int lastread = -1; + + public SmartReader(Reader r) { reader = new PushbackReader(r); } + public void unread() throws IOException { unread((char)lastread); } + public void unread(char c) throws IOException { + reader.unread(c); + if(c == '\n') col = -1; + else col--; + if (accumulator != null) accumulator.setLength(accumulator.length() - 1); + } + public boolean match(char c) throws IOException { if (peek() == c) { reader.read(); return true; } else return false; } + public int peek() throws IOException { + int peeked = reader.read(); + if (peeked != -1) reader.unread((char)peeked); + return peeked; + } + public int read() throws IOException { + lastread = reader.read(); + if (accumulator != null) accumulator.append((char)lastread); + if (lastread != '\n' && lastread != '\r') col++; + if (lastread == '\n') { + // col is -1 if we just unread a newline, this is sort of ugly + if (col != -1) parserLine = ++line; + col = 0; + } + return lastread; + } + + // FEATURE: could be much more efficient + StringBuffer accumulator = null; + public void startString() { + accumulator = new StringBuffer(); + accumulator.append((char)lastread); + } + public String getString() throws IOException { + String ret = accumulator.toString().intern(); + accumulator = null; + return ret; + } + } + + + // Token PushBack code //////////////////////////////////////////////////////////// + + private int pushBackDepth = 0; + private int[] pushBackInts = new int[10]; + private Object[] pushBackObjects = new Object[10]; + + /** push back a token */ + public final void pushBackToken(int op, Object obj) { + if (pushBackDepth >= pushBackInts.length - 1) { + int[] newInts = new int[pushBackInts.length * 2]; + System.arraycopy(pushBackInts, 0, newInts, 0, pushBackInts.length); + pushBackInts = newInts; + Object[] newObjects = new Object[pushBackObjects.length * 2]; + System.arraycopy(pushBackObjects, 0, newObjects, 0, pushBackObjects.length); + pushBackObjects = newObjects; + } + pushBackInts[pushBackDepth] = op; + pushBackObjects[pushBackDepth] = obj; + pushBackDepth++; + } + + /** push back the most recently read token */ + public void pushBackToken() { pushBackToken(op, number != null ? (Object)number : (Object)string); } + + /** read a token but leave it in the stream */ + public final int peekToken() throws IOException { + int ret = getToken(); + pushBackToken(); + return ret; + } + + /** read a token but leave it in the stream */ + public final int peekNextLine() throws IOException { + int ret = getToken(); + int l = line; + pu... [truncated message content] |