[Setting] Only expose experimental features that are compiled in
[WebKit-https.git] / PerformanceTests / RexBench / Basic / lexer.js
1 /*
2  * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25 "use strict";
26
27 // Loosely based on ECMA 55 sections 4-8, but loosened to allow for modern conventions, like
28 // multi-character variable names. But this doesn't go too far - in particular, this doesn't do
29 // unicode, because that would require more thought.
30 function* lex(string)
31 {
32     let sourceLineNumber = 0;
33     for (let line of string.split("\n")) {
34         ++sourceLineNumber;
35         
36         function consumeWhitespace()
37         {
38             if (/^\s+/.test(line))
39                 line = RegExp.rightContext;
40         }
41    
42         function consume(kind)
43         {
44             line = RegExp.rightContext;
45             return {kind, string: RegExp.lastMatch, sourceLineNumber, userLineNumber};
46         }
47         
48         const isIdentifier = /^[a-z_]([a-z0-9_]*)/i;
49         const isNumber = /^(([0-9]+(\.([0-9]*))?)|(\.[0-9]+)(e([+-]?)([0-9]+))?)/i;
50         const isString = /^\"([^\"]|(\"\"))*\"/;
51         const isKeyword = /^((base)|(data)|(def)|(dim)|(end)|(for)|(go)|(gosub)|(goto)|(if)|(input)|(let)|(next)|(on)|(option)|(print)|(randomize)|(read)|(restore)|(return)|(step)|(stop)|(sub)|(then)|(to))/i;
52         const isOperator = /^(-|\+|\*|\/|\^|\(|\)|(<[>=]?)|(>=?)|=|,|\$|;)/;
53         const isRem = /^rem\s.*/;
54         
55         consumeWhitespace();
56         
57         if (!/^[0-9]+/.test(line))
58             throw new Error("At line " + sourceLineNumber + ": Expect line number: " + line);
59         let userLineNumber = +RegExp.lastMatch;
60         line = RegExp.rightContext;
61         yield {kind: "userLineNumber", string: RegExp.lastMatch, sourceLineNumber, userLineNumber};
62         
63         consumeWhitespace();
64         
65         while (line.length) {
66             if (isKeyword.test(line))
67                 yield consume("keyword");
68             else if (isIdentifier.test(line))
69                 yield consume("identifier");
70             else if (isNumber.test(line)) {
71                 let token = consume("number");
72                 token.value = +token.string;
73                 yield token;
74             } else if (isString.test(line)) {
75                 let token = consume("string");
76                 token.value = "";
77                 for (let i = 1; i < token.string.length - 1; ++i) {
78                     let char = token.string.charAt(i);
79                     if (char == "\"")
80                         i++;
81                     token.value += char;
82                 }
83                 yield token;
84             } else if (isOperator.test(line))
85                 yield consume("operator");
86             else if (isRem.test(line))
87                 yield consume("remark");
88             else
89                 throw new Error("At line " + sourceLineNumber + ": Cannot lex token: " + line);
90             consumeWhitespace();
91         }
92         
93         // Note: this is necessary for the parser, which may look-ahead without checking if we're
94         // done. Fortunately, it won't look-ahead past a newLine.
95         yield {kind: "newLine", string:"\n", sourceLineNumber, userLineNumber};
96     }
97 }