Changeset 235738 in webkit


Ignore:
Timestamp:
Sep 6, 2018 9:01:05 AM (6 years ago)
Author:
mmaxfield@apple.com
Message:

[WHLSL] The parser is too slow
https://bugs.webkit.org/show_bug.cgi?id=189014

Reviewed by Filip Pizlo.

This patch includes three changes:

  1. Migrate from using try/catch to simply returning the WSyntaxError. This means that each parser call has to check for this sentinel value. The lexer still can throw if it encounters an unknown token or an unmatched "/*" token (which is rare).
  2. After removing try/catch, making the sentinel values not inherit from Error (the Error constructor was taking lots of time)
  3. Previously, every time the parser failed (which is many times per expression) it was running a regex over the entire source text to figure out where the error occurred. Instead, we can preprocess the text string to find these line numbers ahead of time.

Together, these make the parser 75x faster. Parsing the standard library goes from 2.5
hours down to 2 minutes. Because it's now a reasonable length, this patch uncomments
the bulk of the standard library.

  • WebGPUShadingLanguageRI/All.js:
  • WebGPUShadingLanguageRI/Lexer.js:

(Lexer):
(Lexer.prototype.lineNumberForIndex):

  • WebGPUShadingLanguageRI/Parse.js:

(fail):
(backtrackingScope):
(testScope):
(genericConsume):
(consumeEndOfTypeArgs):
(parseTerm):
(parseConstexpr):
(parseTypeArguments):
(parseType.getAddressSpace):
(parseType):
(parseTypeDef):
(genericParseLeft):
(parseCallExpression.let.parseArguments):
(isCallExpression):
(parseSuffixOperator):
(parsePossibleSuffix):
(parsePreIncrement):
(parsePossiblePrefix):
(parsePossibleTernaryConditional):
(parsePossibleAssignment):
(parsePostIncrement):
(parseEffectfulExpression):
(genericParseCommaExpression):
(parseEffectfulStatement):
(parseReturn):
(parseBreak):
(parseContinue):
(parseIfStatement):
(parseWhile):
(parseFor):
(parseDo):
(parseVariableDecls):
(parseSwitchCase):
(parseSwitchStatement):
(parseStatement):
(parseBlockBody):
(parseBlock):
(parseParameter):
(parseFuncName):
(parseFuncDecl):
(parseFuncDef):
(parseField):
(parseStructType):
(parseNativeFunc):
(parseNative):
(parseRestrictedFuncDef):
(parseEnumMember):
(parseEnumType):
(parse):

  • WebGPUShadingLanguageRI/SPIRV.html:
  • WebGPUShadingLanguageRI/StandardLibrary.js:

(let.standardLibrary):

  • WebGPUShadingLanguageRI/Test.html:
  • WebGPUShadingLanguageRI/Test.js:

(checkFail.doPrep): Deleted.

  • WebGPUShadingLanguageRI/WLexicalError.js: Added.

(WLexicalError):

  • WebGPUShadingLanguageRI/index.html:
Location:
trunk/Tools
Files:
11 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/Tools/ChangeLog

    r235734 r235738  
     12018-09-06  Myles C. Maxfield  <mmaxfield@apple.com>
     2
     3        [WHLSL] The parser is too slow
     4        https://bugs.webkit.org/show_bug.cgi?id=189014
     5
     6        Reviewed by Filip Pizlo.
     7
     8        This patch includes three changes:
     9        1. Migrate from using try/catch to simply returning the WSyntaxError. This means that
     10           each parser call has to check for this sentinel value. The lexer still can throw if
     11           it encounters an unknown token or an unmatched "/*" token (which is rare).
     12        2. After removing try/catch, making the sentinel values not inherit from Error (the
     13           Error constructor was taking lots of time)
     14        3. Previously, every time the parser failed (which is many times per expression) it was
     15           running a regex over the entire source text to figure out where the error occurred.
     16           Instead, we can preprocess the text string to find these line numbers ahead of time.
     17
     18        Together, these make the parser 75x faster. Parsing the standard library goes from 2.5
     19        hours down to 2 minutes. Because it's now a reasonable length, this patch uncomments
     20        the bulk of the standard library.
     21
     22        * WebGPUShadingLanguageRI/All.js:
     23        * WebGPUShadingLanguageRI/Lexer.js:
     24        (Lexer):
     25        (Lexer.prototype.lineNumberForIndex):
     26        * WebGPUShadingLanguageRI/Parse.js:
     27        (fail):
     28        (backtrackingScope):
     29        (testScope):
     30        (genericConsume):
     31        (consumeEndOfTypeArgs):
     32        (parseTerm):
     33        (parseConstexpr):
     34        (parseTypeArguments):
     35        (parseType.getAddressSpace):
     36        (parseType):
     37        (parseTypeDef):
     38        (genericParseLeft):
     39        (parseCallExpression.let.parseArguments):
     40        (isCallExpression):
     41        (parseSuffixOperator):
     42        (parsePossibleSuffix):
     43        (parsePreIncrement):
     44        (parsePossiblePrefix):
     45        (parsePossibleTernaryConditional):
     46        (parsePossibleAssignment):
     47        (parsePostIncrement):
     48        (parseEffectfulExpression):
     49        (genericParseCommaExpression):
     50        (parseEffectfulStatement):
     51        (parseReturn):
     52        (parseBreak):
     53        (parseContinue):
     54        (parseIfStatement):
     55        (parseWhile):
     56        (parseFor):
     57        (parseDo):
     58        (parseVariableDecls):
     59        (parseSwitchCase):
     60        (parseSwitchStatement):
     61        (parseStatement):
     62        (parseBlockBody):
     63        (parseBlock):
     64        (parseParameter):
     65        (parseFuncName):
     66        (parseFuncDecl):
     67        (parseFuncDef):
     68        (parseField):
     69        (parseStructType):
     70        (parseNativeFunc):
     71        (parseNative):
     72        (parseRestrictedFuncDef):
     73        (parseEnumMember):
     74        (parseEnumType):
     75        (parse):
     76        * WebGPUShadingLanguageRI/SPIRV.html:
     77        * WebGPUShadingLanguageRI/StandardLibrary.js:
     78        (let.standardLibrary):
     79        * WebGPUShadingLanguageRI/Test.html:
     80        * WebGPUShadingLanguageRI/Test.js:
     81        (checkFail.doPrep): Deleted.
     82        * WebGPUShadingLanguageRI/WLexicalError.js: Added.
     83        (WLexicalError):
     84        * WebGPUShadingLanguageRI/index.html:
     85
    1862018-09-06  Xan Lopez  <xan@igalia.com>
    287
  • trunk/Tools/WebGPUShadingLanguageRI/All.js

    r235635 r235738  
    169169load("VectorType.js");
    170170load("VisitingSet.js");
     171load("WLexicalError.js");
    171172load("WSyntaxError.js");
    172173load("WTrapError.js");
  • trunk/Tools/WebGPUShadingLanguageRI/Lexer.js

    r235096 r235738  
    3636        this._index = 0;
    3737        this._stack = [];
     38
     39        this._lineNumbers = [];
     40        let lineNumber = 1;
     41        for (let i = 0; i < this._text.length; ++i) {
     42            this._lineNumbers.push(lineNumber);
     43            if (this._text[i] == '\n')
     44                ++lineNumber;
     45        }
    3846    }
    3947   
     
    5462    lineNumberForIndex(index)
    5563    {
    56         let matches = this._text.substring(0, index).match(/\n/g);
    57         return (matches ? matches.length : 0) + this._lineNumberOffset;
     64        return this._lineNumbers[index] + this._lineNumberOffset;
    5865    }
    5966   
     
    167174    fail(error)
    168175    {
    169         throw new WSyntaxError(this.originString, error);
    170     }
    171    
    172     backtrackingScope(callback)
    173     {
    174         let state = this.state;
    175         try {
    176             return callback();
    177         } catch (e) {
    178             if (e instanceof WSyntaxError) {
    179                 this.state = state;
    180                 return null;
    181             }
    182             throw e;
    183         }
    184     }
    185    
    186     testScope(callback)
    187     {
    188         let state = this.state;
    189         try {
    190             callback();
    191             return true;
    192         } catch (e) {
    193             if (e instanceof WSyntaxError)
    194                 return false;
    195             throw e;
    196         } finally {
    197             this.state = state;
    198         }
     176        throw new WLexicalError(this.originString, error);
    199177    }
    200178}
  • trunk/Tools/WebGPUShadingLanguageRI/LexerToken.js

    r222351 r235738  
    7171    get originString()
    7272    {
    73         return this.origin + ":" + (this.lineNumber + 1);
     73        return this.origin + ":" + this.lineNumber;
    7474    }
    7575   
  • trunk/Tools/WebGPUShadingLanguageRI/Parse.js

    r235533 r235738  
    2828{
    2929    let lexer = new Lexer(origin, originKind, lineNumberOffset, text);
    30    
     30
    3131    // The hardest part of dealing with C-like languages is parsing variable declaration statements.
    3232    // Let's consider if this happens in WSL. Here are the valid statements in WSL that being with an
     
    6262    // have arbitrary expressions as the callee. The remaining two problems are solved by
    6363    // backtracking. In all other respects, this is a simple recursive descent parser.
    64    
     64
     65    function fail(error)
     66    {
     67        return new WSyntaxError(lexer.originString, error);
     68    }
     69
     70    function backtrackingScope(callback)
     71    {
     72        let state = lexer.state;
     73        let maybeError = callback();
     74        if (maybeError instanceof WSyntaxError) {
     75            lexer.state = state;
     76            return null;
     77        } else
     78            return maybeError;
     79    }
     80
     81    function testScope(callback)
     82    {
     83        let state = lexer.state;
     84        let maybeError = callback();
     85        lexer.state = state;
     86        return !(maybeError instanceof WSyntaxError);
     87    }
     88
    6589    function genericConsume(callback, explanation)
    6690    {
    6791        let token = lexer.next();
    6892        if (!token)
    69             lexer.fail("Unexpected end of file");
     93            return fail("Unexpected end of file");
    7094        if (!callback(token))
    71             lexer.fail("Unexpected token: " + token.text + "; expected: " + explanation);
     95            return fail("Unexpected token: " + token.text + "; expected: " + explanation);
    7296        return token;
    7397    }
    74    
     98
    7599    function consume(...texts)
    76100    {
    77101        return genericConsume(token => texts.includes(token.text), texts);
    78102    }
    79    
     103
    80104    function consumeKind(kind)
    81105    {
    82106        return genericConsume(token => token.kind == kind, kind);
    83107    }
    84    
     108
    85109    function assertNext(...texts)
    86110    {
    87         lexer.push(consume(...texts));
    88     }
    89    
     111        let maybeError = consume(...texts);
     112        if (maybeError instanceof WSyntaxError)
     113            return maybeError;
     114        lexer.push(maybeError);
     115    }
     116
    90117    function genericTest(callback)
    91118    {
     
    95122        return null;
    96123    }
    97    
     124
    98125    function test(...texts)
    99126    {
    100127        return genericTest(token => texts.includes(token.text));
    101128    }
    102    
     129
    103130    function testKind(kind)
    104131    {
    105132        return genericTest(token => token.kind == kind);
    106133    }
    107    
     134
    108135    function tryConsume(...texts)
    109136    {
     
    113140        return result;
    114141    }
    115    
     142
    116143    function tryConsumeKind(kind)
    117144    {
     
    121148        return result;
    122149    }
    123    
     150
    124151    function consumeEndOfTypeArgs()
    125152    {
     
    127154        if (rightShift)
    128155            lexer.push(new LexerToken(lexer, rightShift, rightShift.index, rightShift.kind, ">"));
    129         else
    130             consume(">");
    131     }
    132    
     156        else {
     157            let maybeError = consume(">");
     158            if (maybeError instanceof WSyntaxError)
     159                return maybeError;
     160        }
     161    }
     162
    133163    function parseTerm()
    134164    {
     
    141171            let intVersion = (+token.text) | 0;
    142172            if ("" + intVersion !== token.text)
    143                 lexer.fail("Integer literal is not an integer: " + token.text);
     173                return fail("Integer literal is not an integer: " + token.text);
    144174            return new IntLiteral(token, intVersion);
    145175        }
     
    147177            let uintVersion = token.text.substr(0, token.text.length - 1) >>> 0;
    148178            if (uintVersion + "u" !== token.text)
    149                 lexer.fail("Integer literal is not 32-bit unsigned integer: " + token.text);
     179                return fail("Integer literal is not 32-bit unsigned integer: " + token.text);
    150180            return new UintLiteral(token, uintVersion);
    151181        }
     
    163193                intVersion = intVersion >>> 0;
    164194            if (intVersion.toString(16) !== hexString)
    165                 lexer.fail("Hex integer literal is not an integer: " + token.text);
     195                return fail("Hex integer literal is not an integer: " + token.text);
    166196            if (token.kind == "intHexLiteral")
    167197                return new IntLiteral(token, intVersion);
     
    179209            return new BoolLiteral(token, token.text == "true");
    180210        // FIXME: Need support for other literals too.
    181         consume("(");
     211        let maybeError = consume("(");
     212        if (maybeError instanceof WSyntaxError)
     213            return maybeError;
    182214        let result = parseExpression();
    183         consume(")");
     215        if (result instanceof WSyntaxError)
     216            return result;
     217        maybeError = consume(")");
     218        if (maybeError instanceof WSyntaxError)
     219            return maybeError;
    184220        return result;
    185221    }
    186    
     222
    187223    function parseConstexpr()
    188224    {
    189225        let token;
    190         if (token = tryConsume("-"))
    191             return new CallExpression(token, "operator" + token.text, [parseTerm()]);
     226        if (token = tryConsume("-")) {
     227            let term = parseTerm();
     228            if (term instanceof WSyntaxError)
     229                return term;
     230            return new CallExpression(token, "operator" + token.text, [term]);
     231        }
    192232        let left = parseTerm();
    193         if (token = tryConsume("."))
    194             left = new DotExpression(token, left, consumeKind("identifier").text);
     233        if (left instanceof WSyntaxError)
     234            return left;
     235        if (token = tryConsume(".")) {
     236            let maybeError = consumeKind("identifier")
     237            if (maybeError instanceof WSyntaxError)
     238                return maybeError;
     239            left = new DotExpression(token, left, maybeError.text);
     240        }
    195241        return left;
    196242    }
    197    
     243
    198244    function parseTypeArguments()
    199245    {
     
    202248
    203249        let result = [];
    204         consume("<");
     250        let maybeError = consume("<");
     251        if (maybeError instanceof WSyntaxError)
     252            return maybeError;
    205253        while (!test(">")) {
    206254            // It's possible for a constexpr or type can syntactically overlap in the single
     
    215263            // the problem that something of the form T[1][2][3]... can either be a type or a
    216264            // constexpr, and we can figure out in the checker which it is.
    217             let typeRef = lexer.backtrackingScope(() => {
     265            let typeRef = backtrackingScope(() => {
    218266                let result = consumeKind("identifier");
    219                 assertNext(",", ">", ">>");
     267                if (result instanceof WSyntaxError)
     268                    return result;
     269                let maybeError = assertNext(",", ">", ">>");
     270                if (maybeError instanceof WSyntaxError)
     271                    return maybeError;
    220272                return new TypeRef(result, result.text);
    221273            });
     
    223275                result.push(typeRef);
    224276            else {
    225                 let constexpr = lexer.backtrackingScope(() => {
     277                let constexpr = backtrackingScope(() => {
    226278                    let result = parseConstexpr();
    227                     assertNext(",", ">", ">>");
     279                    if (result instanceof WSyntaxError)
     280                        return result;
     281                    let maybeError = assertNext(",", ">", ">>");
     282                    if (maybeError instanceof WSyntaxError)
     283                        return maybeError;
    228284                    return result;
    229285                });
    230286                if (constexpr)
    231287                    result.push(constexpr);
    232                 else
    233                     result.push(parseType());
     288                else {
     289                    let type = parseType();
     290                    if (type instanceof WSyntaxError)
     291                        return type;
     292                    result.push(type);
     293                }
    234294            }
    235295            if (!tryConsume(","))
    236296                break;
    237297        }
    238         consumeEndOfTypeArgs();
     298        maybeError = consumeEndOfTypeArgs();
     299        if (maybeError instanceof WSyntaxError)
     300            return maybeError;
    239301        return result;
    240302    }
    241    
     303
    242304    function parseType()
    243305    {
     
    247309        if (token = tryConsume(...addressSpaces))
    248310            addressSpace = token.text;
    249        
     311
    250312        let name = consumeKind("identifier");
     313        if (name instanceof WSyntaxError)
     314            return name;
    251315        let typeArguments = parseTypeArguments();
     316        if (typeArguments instanceof WSyntaxError)
     317            return typeArguments;
    252318        let type = new TypeRef(name, name.text, typeArguments);
    253        
     319
    254320        function getAddressSpace()
    255321        {
     
    257323            if (addressSpace)
    258324                return addressSpace;
    259             return consume(...addressSpaces).text;
    260         }
    261        
     325            let consumedAddressSpace = consume(...addressSpaces);
     326            if (consumedAddressSpace instanceof WSyntaxError)
     327                return consumedAddressSpace;
     328            return consumedAddressSpace.text;
     329        }
     330
    262331        const typeConstructorStack = [ ];
    263332
     
    266335                // Likewise, the address space must be parsed before parsing continues.
    267336                const addressSpace = getAddressSpace();
     337                if (addressSpace instanceof WSyntaxError)
     338                    return addressSpace;
    268339                typeConstructorStack.unshift(type => new PtrType(token, addressSpace, type));
    269340                continue;
     
    272343            if (tryConsume("]")) {
    273344                const addressSpace = getAddressSpace();
     345                if (addressSpace instanceof WSyntaxError)
     346                    return addressSpace;
    274347                typeConstructorStack.unshift(type => new ArrayRefType(token, addressSpace, type));
    275348                continue;
     
    277350
    278351            const lengthExpr = parseConstexpr();
     352            if (lengthExpr instanceof WSyntaxError)
     353                return lengthExpr;
    279354            typeConstructorStack.unshift(type => new ArrayType(token, type, lengthExpr));
    280             consume("]");
     355            let maybeError = consume("]");
     356            if (maybeError instanceof WSyntaxError)
     357                return maybeError;
    281358        }
    282359
     
    285362
    286363        if (addressSpace && !addressSpaceConsumed)
    287             lexer.fail("Address space specified for type that does not need address space");
     364            return fail("Address space specified for type that does not need address space");
    288365
    289366        return type;
    290367    }
    291    
     368
    292369    function parseTypeDef()
    293370    {
    294371        let origin = consume("typedef");
    295         let name = consumeKind("identifier").text;
    296         consume("=");
     372        if (origin instanceof WSyntaxError)
     373            return origin;
     374        let maybeError = consumeKind("identifier");
     375        if (maybeError instanceof WSyntaxError)
     376            return maybeError;
     377        let name = maybeError.text;
     378        maybeError = consume("=");
     379        if (maybeError instanceof WSyntaxError)
     380            return maybeError;
    297381        let type = parseType();
    298         consume(";");
     382        if (type instanceof WSyntaxError)
     383            return type;
     384        maybeError = consume(";");
     385        if (maybeError instanceof WSyntaxError)
     386            return maybeError;
    299387        return new TypeDef(origin, name, type);
    300388    }
    301    
     389
    302390    function genericParseLeft(texts, nextParser, constructor)
    303391    {
    304392        let left = nextParser();
     393        if (left instanceof WSyntaxError)
     394            return left;
    305395        let token;
    306396        while (token = tryConsume(...texts))
     
    308398        return left;
    309399    }
    310    
     400
    311401    function parseLeftOperatorCall(texts, nextParser)
    312402    {
     
    316406                new CallExpression(token, "operator" + token.text, [left, right]));
    317407    }
    318    
     408
    319409    function parseCallExpression()
    320410    {
     
    323413            while (!test(")")) {
    324414                let argument = parsePossibleAssignment();
     415                if (argument instanceof WSyntaxError)
     416                    return argument;
    325417                argumentList.push(argument);
    326418                if (!tryConsume(","))
    327419                    break;
    328420            }
    329             consume(")");
     421            let maybeError = consume(")");
     422            if (maybeError instanceof WSyntaxError)
     423                return maybeError;
    330424            return new CallExpression(origin, callName, argumentList);
    331425        }
    332426
    333         let name = lexer.backtrackingScope(() => {
     427        let name = backtrackingScope(() => {
    334428            let name = consumeKind("identifier");
    335             consume("(");
     429            if (name instanceof WSyntaxError)
     430                return name;
     431            let maybeError = consume("(");
     432            if (maybeError instanceof WSyntaxError)
     433                return maybeError;
    336434            return name;
    337435        });
     
    342440        } else {
    343441            let returnType = parseType();
    344             consume("(");
     442            if (returnType instanceof WSyntaxError)
     443                return returnType;
     444            let maybeError = consume("(");
     445            if (maybeError instanceof WSyntaxError)
     446                return maybeError;
    345447            let result = parseArguments(returnType.origin, "operator cast");
    346448            result.setCastData(returnType);
     
    348450        }
    349451    }
    350    
     452
    351453    function isCallExpression()
    352454    {
    353         return lexer.testScope(() => {
    354                 consumeKind("identifier");
    355                 consume("(");
    356             }) || lexer.testScope(() => {
    357                 parseType();
    358                 consume("(");
     455        return testScope(() => {
     456                let maybeError = consumeKind("identifier");
     457                if (maybeError instanceof WSyntaxError)
     458                    return maybeError;
     459                maybeError = consume("(");
     460                if (maybeError instanceof WSyntaxError)
     461                    return maybeError;
     462            }) || testScope(() => {
     463                let type = parseType();
     464                if (type instanceof WSyntaxError)
     465                    return type;
     466                let maybeError = consume("(");
     467                if (maybeError instanceof WSyntaxError)
     468                    return maybeError;
    359469            });
    360470    }
    361    
     471
    362472    function emitIncrement(token, old, extraArg)
    363473    {
     
    365475        if (extraArg)
    366476            args.push(extraArg);
    367        
     477
    368478        let name = "operator" + token.text;
    369479        if (/=$/.test(name))
    370480            name = RegExp.leftContext;
    371        
     481
    372482        if (name == "operator")
    373483            throw new Error("Invalid name: " + name);
    374        
     484
    375485        return new CallExpression(token, name, args);
    376486    }
    377    
     487
    378488    function finishParsingPostIncrement(token, left)
    379489    {
     
    383493        return readModifyWrite;
    384494    }
    385    
     495
    386496    function parseSuffixOperator(left, acceptableOperators)
    387497    {
     
    396506                if (token.text == "->")
    397507                    left = new DereferenceExpression(token, left);
    398                 left = new DotExpression(token, left, consumeKind("identifier").text);
     508                let maybeError = consumeKind("identifier");
     509                if (maybeError instanceof WSyntaxError)
     510                    return maybeError;
     511                left = new DotExpression(token, left, maybeError.text);
    399512                break;
    400513            case "[": {
    401514                let index = parseExpression();
    402                 consume("]");
     515                if (index instanceof WSyntaxError)
     516                    return index;
     517                let maybeError = consume("]");
     518                if (maybeError instanceof WSyntaxError)
     519                    return maybeError;
    403520                left = new IndexExpression(token, left, index);
    404521                break;
     
    410527        return left;
    411528    }
    412    
     529
    413530    function parsePossibleSuffix()
    414531    {
     
    418535        if (isCallExpression()) {
    419536            left = parseCallExpression();
     537            if (left instanceof WSyntaxError)
     538                return left;
    420539            acceptableOperators = limitedOperators;
    421         } else
     540        } else {
    422541            left = parseTerm();
    423        
     542            if (left instanceof WSyntaxError)
     543                return left;
     544        }
     545
    424546        return parseSuffixOperator(left, acceptableOperators);
    425547    }
    426    
     548
    427549    function finishParsingPreIncrement(token, left, extraArg)
    428550    {
     
    432554        return readModifyWrite;
    433555    }
    434    
     556
    435557    function parsePreIncrement()
    436558    {
    437559        let token = consume("++", "--");
     560        if (token instanceof WSyntaxError)
     561            return token;
    438562        let left = parsePossiblePrefix();
     563        if (left instanceof WSyntaxError)
     564            return left;
    439565        return finishParsingPreIncrement(token, left);
    440566    }
    441    
     567
    442568    function parsePossiblePrefix()
    443569    {
     
    445571        if (test("++", "--"))
    446572            return parsePreIncrement();
    447         if (token = tryConsume("+", "-", "~"))
    448             return new CallExpression(token, "operator" + token.text, [parsePossiblePrefix()]);
    449         if (token = tryConsume("*"))
    450             return new DereferenceExpression(token, parsePossiblePrefix());
    451         if (token = tryConsume("&"))
    452             return new MakePtrExpression(token, parsePossiblePrefix());
    453         if (token = tryConsume("@"))
    454             return new MakeArrayRefExpression(token, parsePossiblePrefix());
    455         if (token = tryConsume("!")) {
     573        if (token = tryConsume("+", "-", "~")) {
     574            let possiblePrefix = parsePossiblePrefix();
     575            if (possiblePrefix instanceof WSyntaxError)
     576                return WSyntaxError;
     577            return new CallExpression(token, "operator" + token.text, [possiblePrefix]);
     578        } if (token = tryConsume("*")) {
     579            let possiblePrefix = parsePossiblePrefix();
     580            if (possiblePrefix instanceof WSyntaxError)
     581                return WSyntaxError;
     582            return new DereferenceExpression(token, possiblePrefix);
     583        } if (token = tryConsume("&")) {
     584            let possiblePrefix = parsePossiblePrefix();
     585            if (possiblePrefix instanceof WSyntaxError)
     586                return WSyntaxError;
     587            return new MakePtrExpression(token, possiblePrefix);
     588        } if (token = tryConsume("@")) {
     589            let possiblePrefix = parsePossiblePrefix();
     590            if (possiblePrefix instanceof WSyntaxError)
     591                return WSyntaxError;
     592            return new MakeArrayRefExpression(token, possiblePrefix);
     593        } if (token = tryConsume("!")) {
    456594            let remainder = parsePossiblePrefix();
     595            if (remainder instanceof WSyntaxError)
     596                return remainder;
    457597            return new LogicalNot(token, new CallExpression(remainder.origin, "bool", [remainder]));
    458598        }
    459599        return parsePossibleSuffix();
    460600    }
    461    
     601
    462602    function parsePossibleProduct()
    463603    {
    464604        return parseLeftOperatorCall(["*", "/", "%"], parsePossiblePrefix);
    465605    }
    466    
     606
    467607    function parsePossibleSum()
    468608    {
    469609        return parseLeftOperatorCall(["+", "-"], parsePossibleProduct);
    470610    }
    471    
     611
    472612    function parsePossibleShift()
    473613    {
    474614        return parseLeftOperatorCall(["<<", ">>"], parsePossibleSum);
    475615    }
    476    
     616
    477617    function parsePossibleRelationalInequality()
    478618    {
    479619        return parseLeftOperatorCall(["<", ">", "<=", ">="], parsePossibleShift);
    480620    }
    481    
     621
    482622    function parsePossibleRelationalEquality()
    483623    {
     
    491631            });
    492632    }
    493    
     633
    494634    function parsePossibleBitwiseAnd()
    495635    {
    496636        return parseLeftOperatorCall(["&"], parsePossibleRelationalEquality);
    497637    }
    498    
     638
    499639    function parsePossibleBitwiseXor()
    500640    {
    501641        return parseLeftOperatorCall(["^"], parsePossibleBitwiseAnd);
    502642    }
    503    
     643
    504644    function parsePossibleBitwiseOr()
    505645    {
    506646        return parseLeftOperatorCall(["|"], parsePossibleBitwiseXor);
    507647    }
    508    
     648
    509649    function parseLeftLogicalExpression(texts, nextParser)
    510650    {
     
    513653            (token, left, right) => new LogicalExpression(token, token.text, new CallExpression(left.origin, "bool", [left]), new CallExpression(right.origin, "bool", [right])));
    514654    }
    515    
     655
    516656    function parsePossibleLogicalAnd()
    517657    {
    518658        return parseLeftLogicalExpression(["&&"], parsePossibleBitwiseOr);
    519659    }
    520    
     660
    521661    function parsePossibleLogicalOr()
    522662    {
    523663        return parseLeftLogicalExpression(["||"], parsePossibleLogicalAnd);
    524664    }
    525    
     665
    526666    function parsePossibleTernaryConditional()
    527667    {
    528668        let predicate = parsePossibleLogicalOr();
     669        if (predicate instanceof WSyntaxError)
     670            return predicate;
    529671        let operator = tryConsume("?");
    530672        if (!operator)
    531673            return predicate;
    532674        let bodyExpression = parsePossibleAssignment();
    533         consume(":");
     675        if (bodyExpression instanceof WSyntaxError)
     676            return bodyExpression;
     677        let maybeError = consume(":");
     678        if (maybeError instanceof WSyntaxError)
     679            return maybeError;
    534680        let elseExpression = parsePossibleAssignment();
     681        if (elseExpression instanceof WSyntaxError)
     682            return elseExpression;
    535683        return new TernaryExpression(operator, predicate, bodyExpression, elseExpression);
    536684    }
    537    
     685
    538686    function parsePossibleAssignment(mode)
    539687    {
    540688        let lhs = parsePossibleTernaryConditional();
     689        if (lhs instanceof WSyntaxError)
     690            return lhs;
    541691        let operator = tryConsume("=", "+=", "-=", "*=", "/=", "%=", "^=", "|=", "&=");
    542692        if (!operator) {
    543693            if (mode == "required")
    544                 lexer.fail("Expected assignment: " + lexer._text.substring(lexer._index));
     694                return fail("Expected assignment: " + lexer._text.substring(lexer._index));
    545695            return lhs;
    546696        }
    547         if (operator.text == "=")
    548             return new Assignment(operator, lhs, parsePossibleAssignment());
    549         return finishParsingPreIncrement(operator, lhs, parsePossibleAssignment());
    550     }
    551    
     697        if (operator.text == "=") {
     698            let innerAssignment = parsePossibleAssignment();
     699            if (innerAssignment instanceof WSyntaxError)
     700                return innerAssignment;
     701            return new Assignment(operator, lhs, innerAssignment);
     702        }
     703        let innerAssignment = parsePossibleAssignment();
     704        if (innerAssignment instanceof WSyntaxError)
     705            return innerAssignment;
     706        return finishParsingPreIncrement(operator, lhs, innerAssignment);
     707    }
     708
    552709    function parseAssignment()
    553710    {
    554711        return parsePossibleAssignment("required");
    555712    }
    556    
     713
    557714    function parsePostIncrement()
    558715    {
    559         let left = parseSuffixOperator(parseTerm(), ".", "->", "[");
     716        let term = parseTerm();
     717        if (term instanceof WSyntaxError)
     718            return term;
     719        let left = parseSuffixOperator(term, ".", "->", "[");
     720        if (left instanceof WSyntaxError)
     721            return left;
    560722        let token = consume("++", "--");
     723        if (token instanceof WSyntaxError)
     724            return token;
    561725        return finishParsingPostIncrement(token, left);
    562726    }
    563    
     727
    564728    function parseEffectfulExpression()
    565729    {
    566730        if (isCallExpression())
    567731            return parseCallExpression();
    568         let preIncrement = lexer.backtrackingScope(parsePreIncrement);
     732        let preIncrement = backtrackingScope(parsePreIncrement);
    569733        if (preIncrement)
    570734            return preIncrement;
    571         let postIncrement = lexer.backtrackingScope(parsePostIncrement);
     735        let postIncrement = backtrackingScope(parsePostIncrement);
    572736        if (postIncrement)
    573737            return postIncrement;
    574738        return parseAssignment();
    575739    }
    576    
     740
    577741    function genericParseCommaExpression(finalExpressionParser)
    578742    {
     
    580744        let origin = lexer.peek();
    581745        if (!origin)
    582             lexer.fail("Unexpected end of file");
     746            return fail("Unexpected end of file");
    583747        for (;;) {
    584             let effectfulExpression = lexer.backtrackingScope(() => {
    585                 parseEffectfulExpression();
    586                 consume(",");
     748            let effectfulExpression = backtrackingScope(() => {
     749                let effectfulExpression = parseEffectfulExpression();
     750                if (effectfulExpression instanceof WSyntaxError)
     751                    return effectfulExpression;
     752                let maybeError = consume(",");
     753                if (maybeError instanceof WSyntaxError)
     754                    return maybeError;
     755                return effectfulExpression;
    587756            });
    588757            if (!effectfulExpression) {
     
    599768        return new CommaExpression(origin, list);
    600769    }
    601    
     770
    602771    function parseCommaExpression()
    603772    {
    604773        return genericParseCommaExpression(parsePossibleAssignment);
    605774    }
    606    
     775
    607776    function parseExpression()
    608777    {
    609778        return parseCommaExpression();
    610779    }
    611    
     780
    612781    function parseEffectfulStatement()
    613782    {
    614783        let result = genericParseCommaExpression(parseEffectfulExpression);
    615         consume(";");
     784        if (result instanceof WSyntaxError)
     785            return result;
     786        let maybeError = consume(";");
     787        if (maybeError instanceof WSyntaxError)
     788            return maybeError;
    616789        return result;
    617790    }
    618    
     791
    619792    function parseReturn()
    620793    {
    621794        let origin = consume("return");
     795        if (origin instanceof WSyntaxError)
     796            return origin;
    622797        if (tryConsume(";"))
    623798            return new Return(origin, null);
    624799        let expression = parseExpression();
    625         consume(";");
     800        if (expression instanceof WSyntaxError)
     801            return expression;
     802        let maybeError = consume(";");
     803        if (maybeError instanceof WSyntaxError)
     804            return maybeError;
    626805        return new Return(origin, expression);
    627806    }
    628    
     807
    629808    function parseBreak()
    630809    {
    631810        let origin = consume("break");
    632         consume(";");
     811        if (origin instanceof WSyntaxError)
     812            return origin;
     813        let maybeError = consume(";");
     814        if (maybeError instanceof WSyntaxError)
     815            return maybeError;
    633816        return new Break(origin);
    634817    }
    635    
     818
    636819    function parseContinue()
    637820    {
    638821        let origin = consume("continue");
    639         consume(";");
     822        if (origin instanceof WSyntaxError)
     823            return origin;
     824        let maybeError = consume(";");
     825        if (maybeError instanceof WSyntaxError)
     826            return maybeError;
    640827        return new Continue(origin);
    641828    }
     
    644831    {
    645832        let origin = consume("if");
    646         consume("(");
     833        if (origin instanceof WSyntaxError)
     834            return origin;
     835        let maybeError = consume("(");
     836        if (maybeError instanceof WSyntaxError)
     837            return maybeError;
    647838        let conditional = parseExpression();
    648         consume(")");
     839        if (conditional instanceof WSyntaxError)
     840            return conditional;
     841        maybeError = consume(")");
     842        if (maybeError instanceof WSyntaxError)
     843            return maybeError;
    649844        let body = parseStatement();
     845        if (body instanceof WSyntaxError)
     846            return body;
    650847        let elseBody;
    651         if (tryConsume("else"))
     848        if (tryConsume("else")) {
    652849            elseBody = parseStatement();
     850            if (elseBody instanceof WSyntaxError)
     851                return elseBody;
     852        }
    653853        return new IfStatement(origin, new CallExpression(conditional.origin, "bool", [conditional]), body, elseBody);
    654854    }
     
    657857    {
    658858        let origin = consume("while");
    659         consume("(");
     859        if (origin instanceof WSyntaxError)
     860            return origin;
     861        let maybeError = consume("(");
     862        if (maybeError instanceof WSyntaxError)
     863            return maybeError;
    660864        let conditional = parseExpression();
    661         consume(")");
     865        if (conditional instanceof WSyntaxError)
     866            return conditional;
     867        maybeError = consume(")");
     868        if (maybeError instanceof WSyntaxError)
     869            return maybeError;
    662870        let body = parseStatement();
     871        if (body instanceof WSyntaxError)
     872            return body;
    663873        return new WhileLoop(origin, new CallExpression(conditional.origin, "bool", [conditional]), body);
    664874    }
     
    667877    {
    668878        let origin = consume("for");
    669         consume("(");
     879        if (origin instanceof WSyntaxError)
     880            return origin;
     881        let maybeError = consume("(");
     882        if (maybeError instanceof WSyntaxError)
     883            return maybeError;
    670884        let initialization;
    671885        if (tryConsume(";"))
    672886            initialization = undefined;
    673887        else {
    674             initialization = lexer.backtrackingScope(parseVariableDecls);
    675             if (!initialization)
     888            initialization = backtrackingScope(parseVariableDecls);
     889            if (!initialization) {
    676890                initialization = parseEffectfulStatement();
     891                if (initialization instanceof WSyntaxError)
     892                    return initialization;
     893            }
    677894        }
    678895        let condition = tryConsume(";");
     
    681898        else {
    682899            condition = parseExpression();
    683             consume(";");
     900            if (condition instanceof WSyntaxError)
     901                return condition;
     902            maybeError = consume(";");
     903            if (maybeError instanceof WSyntaxError)
     904                return maybeError;
    684905            condition = new CallExpression(condition.origin, "bool", [condition]);
    685906        }
     
    689910        else {
    690911            increment = parseExpression();
    691             consume(")");
     912            if (increment instanceof WSyntaxError)
     913                return increment;
     914            maybeError = consume(")");
     915            if (maybeError instanceof WSyntaxError)
     916                return maybeError;
    692917        }
    693918        let body = parseStatement();
     919        if (body instanceof WSyntaxError)
     920            return body;
    694921        return new ForLoop(origin, initialization, condition, increment, body);
    695922    }
     
    698925    {
    699926        let origin = consume("do");
     927        if (origin instanceof WSyntaxError)
     928            return origin;
    700929        let body = parseStatement();
    701         consume("while");
    702         consume("(");
     930        if (body instanceof WSyntaxError)
     931            return body;
     932        let maybeError = consume("while");
     933        if (maybeError instanceof WSyntaxError)
     934            return maybeError;
     935        maybeError = consume("(");
     936        if (maybeError instanceof WSyntaxError)
     937            return maybeError;
    703938        let conditional = parseExpression();
    704         consume(")");
     939        if (conditional instanceof WSyntaxError)
     940            return conditional;
     941        maybeError = consume(")");
     942        if (maybeError instanceof WSyntaxError)
     943            return maybeError;
    705944        return new DoWhileLoop(origin, body, new CallExpression(conditional.origin, "bool", [conditional]));
    706945    }
    707    
     946
    708947    function parseVariableDecls()
    709948    {
    710949        let type = parseType();
     950        if (type instanceof WSyntaxError)
     951            return type;
    711952        let list = [];
    712953        do {
    713954            let name = consumeKind("identifier");
     955            if (name instanceof WSyntaxError)
     956                return name;
    714957            let initializer = tryConsume("=") ? parseExpression() : null;
     958            if (initializer instanceof WSyntaxError)
     959                return initializer;
    715960            list.push(new VariableDecl(name, name.text, type, initializer));
    716         } while (consume(",", ";").text == ",");
     961            let maybeError = consume(",", ";");
     962            if (maybeError instanceof WSyntaxError)
     963                return maybeError;
     964            if (maybeError.text != ",")
     965                break;
     966        } while (true);
    717967        return new CommaExpression(type.origin, list);
    718968    }
    719    
     969
    720970    function parseSwitchCase()
    721971    {
    722972        let token = consume("default", "case");
     973        if (token instanceof WSyntaxError)
     974            return token;
    723975        let value;
    724         if (token.text == "case")
     976        if (token.text == "case") {
    725977            value = parseConstexpr();
    726         consume(":");
     978            if (value instanceof WSyntaxError)
     979                return value;
     980        }
     981        let maybeError = consume(":");
     982        if (maybeError instanceof WSyntaxError)
     983            return maybeError;
    727984        let body = parseBlockBody("}", "default", "case");
     985        if (body instanceof WSyntaxError)
     986            return body;
    728987        return new SwitchCase(token, value, body);
    729988    }
    730    
     989
    731990    function parseSwitchStatement()
    732991    {
    733992        let origin = consume("switch");
    734         consume("(");
     993        if (origin instanceof WSyntaxError)
     994            return origin;
     995        let maybeError = consume("(");
     996        if (maybeError instanceof WSyntaxError)
     997            return maybeError;
    735998        let value = parseExpression();
    736         consume(")");
    737         consume("{");
     999        if (value instanceof WSyntaxError)
     1000            return value;
     1001        maybeError = consume(")");
     1002        if (maybeError instanceof WSyntaxError)
     1003            return maybeError;
     1004        maybeError = consume("{");
     1005        if (maybeError instanceof WSyntaxError)
     1006            return maybeError;
    7381007        let result = new SwitchStatement(origin, value);
    739         while (!tryConsume("}"))
    740             result.add(parseSwitchCase());
     1008        while (!tryConsume("}")) {
     1009            let switchCase = parseSwitchCase();
     1010            if (switchCase instanceof WSyntaxError)
     1011                return switchCase;
     1012            result.add(switchCase);
     1013        }
    7411014        return result;
    7421015    }
    743    
     1016
    7441017    function parseStatement()
    7451018    {
     
    7671040        if (token.text == "trap") {
    7681041            let origin = consume("trap");
     1042            if (origin instanceof WSyntaxError)
     1043                return origin;
    7691044            consume(";");
    7701045            return new TrapStatement(origin);
     
    7721047        if (token.text == "{")
    7731048            return parseBlock();
    774         let variableDecl = lexer.backtrackingScope(parseVariableDecls);
     1049        let variableDecl = backtrackingScope(parseVariableDecls);
    7751050        if (variableDecl)
    7761051            return variableDecl;
    7771052        return parseEffectfulStatement();
    7781053    }
    779    
     1054
    7801055    function parseBlockBody(...terminators)
    7811056    {
     
    7831058        while (!test(...terminators)) {
    7841059            let statement = parseStatement();
     1060            if (statement instanceof WSyntaxError)
     1061                return statement;
    7851062            if (statement)
    7861063                block.add(statement);
     
    7881065        return block;
    7891066    }
    790    
     1067
    7911068    function parseBlock()
    7921069    {
    7931070        let origin = consume("{");
     1071        if (origin instanceof WSyntaxError)
     1072            return origin;
    7941073        let block = parseBlockBody("}");
    795         consume("}");
     1074        if (block instanceof WSyntaxError)
     1075            return block;
     1076        let maybeError = consume("}");
     1077        if (maybeError instanceof WSyntaxError)
     1078            return maybeError;
    7961079        return block;
    7971080    }
    798    
     1081
    7991082    function parseParameter()
    8001083    {
    8011084        let type = parseType();
     1085        if (type instanceof WSyntaxError)
     1086            return type;
    8021087        let name = tryConsumeKind("identifier");
    8031088        return new FuncParameter(type.origin, name ? name.text : null, type);
    8041089    }
    805    
     1090
    8061091    function parseParameters()
    8071092    {
    808         consume("(");
     1093        let maybeError = consume("(");
     1094        if (maybeError instanceof WSyntaxError)
     1095            return maybeError;
    8091096        let parameters = [];
    8101097        while (!test(")")) {
    811             parameters.push(parseParameter());
     1098            let parameter = parseParameter();
     1099            if (parameter instanceof WSyntaxError)
     1100                return parameter;
     1101            parameters.push(parameter);
    8121102            if (!tryConsume(","))
    8131103                break;
    8141104        }
    815         consume(")");
     1105        maybeError = consume(")");
     1106        if (maybeError instanceof WSyntaxError)
     1107            return maybeError;
    8161108        return parameters;
    8171109    }
    818    
     1110
    8191111    function parseFuncName()
    8201112    {
    8211113        if (tryConsume("operator")) {
    8221114            let token = consume("+", "-", "*", "/", "%", "^", "&", "|", "<", ">", "<=", ">=", "==", "++", "--", ".", "~", "<<", ">>", "[");
     1115            if (token instanceof WSyntaxError)
     1116                return token;
    8231117            if (token.text == "&") {
    8241118                if (tryConsume("[")) {
    825                     consume("]");
     1119                    let maybeError = consume("]");
     1120                    if (maybeError instanceof WSyntaxError)
     1121                        return maybeError;
    8261122                    return "operator&[]";
    8271123                }
    828                 if (tryConsume("."))
    829                     return "operator&." + consumeKind("identifier").text;
     1124                if (tryConsume(".")) {
     1125                    let maybeError = consumeKind("identifier");
     1126                    if (maybeError instanceof WSyntaxError)
     1127                        return maybeError;
     1128                    return "operator&." + maybeError.text;
     1129                }
    8301130                return "operator&";
    8311131            }
    8321132            if (token.text == ".") {
    833                 let result = "operator." + consumeKind("identifier").text;
     1133                let maybeError = consumeKind("identifier");
     1134                if (maybeError instanceof WSyntaxError)
     1135                    return maybeError;
     1136                let result = "operator." + maybeError.text;
    8341137                if (tryConsume("="))
    8351138                    result += "=";
     
    8371140            }
    8381141            if (token.text == "[") {
    839                 consume("]");
     1142                let maybeError = consume("]");
     1143                if (maybeError instanceof WSyntaxError)
     1144                    return maybeError;
    8401145                let result = "operator[]";
    8411146                if (tryConsume("="))
     
    8451150            return "operator" + token.text;
    8461151        }
    847         return consumeKind("identifier").text;
     1152        let maybeError = consumeKind("identifier");
     1153        if (maybeError instanceof WSyntaxError)
     1154            return maybeError;
     1155        return maybeError.text;
    8481156    }
    8491157
     
    8591167            origin = operatorToken;
    8601168            returnType = parseType();
     1169            if (returnType instanceof WSyntaxError)
     1170                return returnType;
    8611171            name = "operator cast";
    8621172            isCast = true;
     
    8641174            shaderType = tryConsume("vertex", "fragment");
    8651175            returnType = parseType();
     1176            if (returnType instanceof WSyntaxError)
     1177                return returnType;
    8661178            if (shaderType) {
    8671179                origin = shaderType;
     
    8701182                origin = returnType.origin;
    8711183            name = parseFuncName();
     1184            if (name instanceof WSyntaxError)
     1185                return name;
    8721186            isCast = false;
    8731187        }
    8741188        let parameters = parseParameters();
     1189        if (parameters instanceof WSyntaxError)
     1190            return parameters;
    8751191        return new Func(origin, name, returnType, parameters, isCast, shaderType);
    8761192    }
    877    
     1193
    8781194    function parseFuncDef()
    8791195    {
    8801196        let func = parseFuncDecl();
     1197        if (func instanceof WSyntaxError)
     1198            return func;
    8811199        let body = parseBlock();
     1200        if (body instanceof WSyntaxError)
     1201            return body;
    8821202        return new FuncDef(func.origin, func.name, func.returnType, func.parameters, body, func.isCast, func.shaderType);
    8831203    }
    884    
     1204
    8851205    function parseField()
    8861206    {
    8871207        let type = parseType();
     1208        if (type instanceof WSyntaxError)
     1209            return type;
    8881210        let name = consumeKind("identifier");
    889         consume(";");
     1211        if (name instanceof WSyntaxError)
     1212            return name;
     1213        let maybeError = consume(";");
     1214        if (maybeError instanceof WSyntaxError)
     1215            return maybeError;
    8901216        return new Field(name, name.text, type);
    8911217    }
    892    
     1218
    8931219    function parseStructType()
    8941220    {
    8951221        let origin = consume("struct");
    896         let name = consumeKind("identifier").text;
    897         let result = new StructType(origin, name);
    898         consume("{");
    899         while (!tryConsume("}"))
    900             result.add(parseField());
     1222        if (origin instanceof WSyntaxError)
     1223            return origin;
     1224        let name = consumeKind("identifier");
     1225        if (name instanceof WSyntaxError)
     1226            return name;
     1227        let result = new StructType(origin, name.text);
     1228        let maybeError = consume("{");
     1229        if (maybeError instanceof WSyntaxError)
     1230            return maybeError;
     1231        while (!tryConsume("}")) {
     1232            let field = parseField();
     1233            if (field instanceof WSyntaxError)
     1234                return field;
     1235            result.add(field);
     1236        }
    9011237        return result;
    9021238    }
    903    
     1239
    9041240    function parseNativeFunc()
    9051241    {
    9061242        let func = parseFuncDecl();
    907         consume(";");
     1243        if (func instanceof WSyntaxError)
     1244            return func;
     1245        let maybeError = consume(";");
     1246        if (maybeError instanceof WSyntaxError)
     1247            return maybeError;
    9081248        return new NativeFunc(func.origin, func.name, func.returnType, func.parameters, func.isCast, func.shaderType);
    9091249    }
    910    
     1250
    9111251    function parseNative()
    9121252    {
    9131253        let origin = consume("native");
     1254        if (origin instanceof WSyntaxError)
     1255            return origin;
    9141256        if (tryConsume("typedef")) {
    9151257            let name = consumeKind("identifier");
     1258            if (name instanceof WSyntaxError)
     1259                return name;
    9161260            let args = parseTypeArguments();
    917             consume(";");
     1261            if (args instanceof WSyntaxError)
     1262                return args;
     1263            let maybeError = consume(";");
     1264            if (maybeError instanceof WSyntaxError)
     1265                return maybeError;
    9181266            return NativeType.create(origin, name.text, args);
    9191267        }
    9201268        return parseNativeFunc();
    9211269    }
    922    
     1270
    9231271    function parseRestrictedFuncDef()
    9241272    {
    925         consume("restricted");
     1273        let maybeError = consume("restricted");
     1274        if (maybeError instanceof WSyntaxError)
     1275            return maybeError;
    9261276        let result;
    927         if (tryConsume("native"))
     1277        if (tryConsume("native")) {
    9281278            result = parseNativeFunc();
    929         else
     1279            if (result instanceof WSyntaxError)
     1280                return result;
     1281        } else {
    9301282            result = parseFuncDef();
     1283            if (result instanceof WSyntaxError)
     1284                return result;
     1285        }
    9311286        result.isRestricted = true;
    9321287        return result;
    9331288    }
    934    
     1289
    9351290    function parseEnumMember()
    9361291    {
    9371292        let name = consumeKind("identifier");
     1293        if (name instanceof WSyntaxError)
     1294            return name;
    9381295        let value = null;
    939         if (tryConsume("="))
     1296        if (tryConsume("=")) {
    9401297            value = parseConstexpr();
     1298            if (value instanceof WSyntaxError)
     1299                return value;
     1300        }
    9411301        return new EnumMember(name, name.text, value);
    9421302    }
    943    
     1303
    9441304    function parseEnumType()
    9451305    {
    946         consume("enum");
     1306        let maybeError = consume("enum");
     1307        if (maybeError instanceof WSyntaxError)
     1308            return maybeError;
    9471309        let name = consumeKind("identifier");
     1310        if (name instanceof WSyntaxError)
     1311            return name;
    9481312        let baseType;
    949         if (tryConsume(":"))
     1313        if (tryConsume(":")) {
    9501314            baseType = parseType();
    951         else
     1315            if (baseType instanceof WSyntaxError)
     1316                return baseType;
     1317        } else
    9521318            baseType = new TypeRef(name, "int");
    953         consume("{");
     1319        maybeError = consume("{");
     1320        if (maybeError instanceof WSyntaxError)
     1321            return maybeError;
    9541322        let result = new EnumType(name, name.text, baseType);
    9551323        while (!test("}")) {
    956             result.add(parseEnumMember());
     1324            let enumMember = parseEnumMember();
     1325            if (enumMember instanceof WSyntaxError)
     1326                return enumMember;
     1327            result.add(enumMember);
    9571328            if (!tryConsume(","))
    9581329                break;
    9591330        }
    960         consume("}");
     1331        maybeError = consume("}");
     1332        if (maybeError instanceof WSyntaxError)
     1333            return maybeError;
    9611334        return result;
    9621335    }
    963    
     1336
    9641337    for (;;) {
    9651338        let token = lexer.peek();
     
    9681341        if (token.text == ";")
    9691342            lexer.next();
    970         else if (token.text == "typedef")
    971             program.add(parseTypeDef());
    972         else if (originKind == "native" && token.text == "native")
    973             program.add(parseNative());
    974         else if (originKind == "native" && token.text == "restricted")
    975             program.add(parseRestrictedFuncDef());
    976         else if (token.text == "struct")
    977             program.add(parseStructType());
    978         else if (token.text == "enum")
    979             program.add(parseEnumType());
    980         else
    981             program.add(parseFuncDef());
     1343        else if (token.text == "typedef") {
     1344            let typeDef = parseTypeDef();
     1345            if (typeDef instanceof WSyntaxError)
     1346                throw typeDef;
     1347            program.add(typeDef);
     1348        } else if (originKind == "native" && token.text == "native") {
     1349            let native = parseNative();
     1350            if (native instanceof WSyntaxError)
     1351                throw native;
     1352            program.add(native);
     1353        } else if (originKind == "native" && token.text == "restricted") {
     1354            let restrictedFuncDef = parseRestrictedFuncDef();
     1355            if (restrictedFuncDef instanceof WSyntaxError)
     1356                throw restrictedFuncDef;
     1357            program.add(restrictedFuncDef);
     1358        } else if (token.text == "struct") {
     1359            let struct = parseStructType();
     1360            if (struct instanceof WSyntaxError)
     1361                throw struct;
     1362            program.add(struct);
     1363        } else if (token.text == "enum") {
     1364            let enumType = parseEnumType();
     1365            if (enumType instanceof WSyntaxError)
     1366                throw enumType;
     1367            program.add(enumType);
     1368        } else {
     1369            let funcDef = parseFuncDef();
     1370            if (funcDef instanceof WSyntaxError)
     1371                throw funcDef;
     1372            program.add(funcDef);
     1373        }
    9821374    }
    9831375}
  • trunk/Tools/WebGPUShadingLanguageRI/SPIRV.html

    r235635 r235738  
    152152    <script src="VectorType.js"></script>
    153153    <script src="VisitingSet.js"></script>
     154    <script src="WLexicalError.js"></script>
    154155    <script src="WSyntaxError.js"></script>
    155156    <script src="WTrapError.js"></script>
  • trunk/Tools/WebGPUShadingLanguageRI/StandardLibrary.js

    r235642 r235738  
    774774            return result;
    775775        }
    776        
    777         /*
     776
    778777        for (let type of [`bool`, `uchar`, `ushort`, `uint`, `char`, `short`, `int`, `half`, `float`]) {
    779778            for (let size of [2, 3, 4]) {
     
    18901889        }
    18911890        print();
    1892        
     1891        /*
    18931892        for (let type of [`uint`, `int`]) {
    18941893            for (let functionName of [`Add`, `And`, `Exchange`, `Max`, `Min`, `Or`, `Xor`]) {
     
    18991898        print();
    19001899        */
    1901 
    19021900        for (let type of [`uchar`, `ushort`, `uint`, `char`, `short`, `int`, `half`, `float`]) {
    19031901            for (let length of [``, `2`, `3`, `4`]) {
  • trunk/Tools/WebGPUShadingLanguageRI/Test.html

    r235635 r235738  
    146146<script src="VectorType.js"></script>
    147147<script src="VisitingSet.js"></script>
     148<script src="WLexicalError.js"></script>
    148149<script src="WSyntaxError.js"></script>
    149150<script src="WTrapError.js"></script>
  • trunk/Tools/WebGPUShadingLanguageRI/Test.js

    r235635 r235738  
    329329    }
    330330});
    331 
    332 tests.commentParsing = function() {
    333     let program = doPrep(`
    334         /* this comment
    335         runs over multiple lines */
    336         bool foo() { return true; }
    337     `);
    338     checkBool(program, callFunction(program, "foo", []), true);
    339 
    340     checkFail(
    341         () => doPrep(`
    342         /* this comment
    343         runs over multiple lines
    344         bool foo() { return true; }
    345         `),
    346         (e) => e instanceof WSyntaxError);
    347 }
    348331
    349332tests.ternaryExpression = function() {
     
    76277610}
    76287611
     7612tests.commentParsing = function() {
     7613    let program = doPrep(`
     7614        /* this comment
     7615        runs over multiple lines */
     7616        bool foo() { return true; }
     7617    `);
     7618    checkBool(program, callFunction(program, "foo", []), true);
     7619
     7620    checkFail(
     7621        () => doPrep(`
     7622        /* this comment
     7623        runs over multiple lines
     7624        bool foo() { return true; }
     7625        `),
     7626        (e) => e instanceof WLexicalError);
     7627}
     7628
    76297629okToTest = true;
    76307630
     
    76847684}
    76857685
    7686 
  • trunk/Tools/WebGPUShadingLanguageRI/WLexicalError.js

    r235737 r235738  
    11/*
    2  * Copyright (C) 2017 Apple Inc. All rights reserved.
     2 * Copyright (C) 2018 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2525"use strict";
    2626
    27 class WSyntaxError extends Error {
     27class WLexicalError extends Error {
    2828    constructor(originString, message)
    2929    {
    30         super("Syntax error at " + originString + ": " + message);
     30        super("Lexical error at " + originString + ": " + message);
    3131        this.originString = originString;
    3232        this.syntaxErrorMessage = message;
  • trunk/Tools/WebGPUShadingLanguageRI/WSyntaxError.js

    r221393 r235738  
    2525"use strict";
    2626
    27 class WSyntaxError extends Error {
     27class WSyntaxError {
    2828    constructor(originString, message)
    2929    {
    30         super("Syntax error at " + originString + ": " + message);
     30        this.payload = "Syntax error at " + originString + ": " + message;
    3131        this.originString = originString;
    3232        this.syntaxErrorMessage = message;
  • trunk/Tools/WebGPUShadingLanguageRI/index.html

    r235635 r235738  
    146146<script src="VectorType.js"></script>
    147147<script src="VisitingSet.js"></script>
     148<script src="WLexicalError.js"></script>
    148149<script src="WSyntaxError.js"></script>
    149150<script src="WTrapError.js"></script>
Note: See TracChangeset for help on using the changeset viewer.