diff options
Diffstat (limited to 'third_party/webkit/PerformanceTests/ARES-6/Basic/parser.js')
-rw-r--r-- | third_party/webkit/PerformanceTests/ARES-6/Basic/parser.js | 558 |
1 files changed, 558 insertions, 0 deletions
diff --git a/third_party/webkit/PerformanceTests/ARES-6/Basic/parser.js b/third_party/webkit/PerformanceTests/ARES-6/Basic/parser.js new file mode 100644 index 0000000000..562e157595 --- /dev/null +++ b/third_party/webkit/PerformanceTests/ARES-6/Basic/parser.js @@ -0,0 +1,558 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +"use strict"; + +function parse(tokenizer) +{ + let program; + + let pushBackBuffer = []; + + function nextToken() + { + if (pushBackBuffer.length) + return pushBackBuffer.pop(); + let result = tokenizer.next(); + if (result.done) + return {kind: "endOfFile", string: "<end of file>"}; + return result.value; + } + + function pushToken(token) + { + pushBackBuffer.push(token); + } + + function peekToken() + { + let result = nextToken(); + pushToken(result); + return result; + } + + function consumeKind(kind) + { + let token = nextToken(); + if (token.kind != kind) { + throw new Error("At " + token.sourceLineNumber + ": expected " + kind + " but got: " + token.string); + } + return token; + } + + function consumeToken(string) + { + let token = nextToken(); + if (token.string.toLowerCase() != string.toLowerCase()) + throw new Error("At " + token.sourceLineNumber + ": expected " + string + " but got: " + token.string); + return token; + } + + function parseVariable() + { + let name = consumeKind("identifier").string; + let result = {evaluate: Basic.Variable, name, parameters: []}; + if (peekToken().string == "(") { + do { + nextToken(); + result.parameters.push(parseNumericExpression()); + } while (peekToken().string == ","); + consumeToken(")"); + } + return result; + } + + function parseNumericExpression() + { + function parsePrimary() + { + let token = nextToken(); + switch (token.kind) { + case "identifier": { + let result = {evaluate: Basic.NumberApply, name: token.string, parameters: []}; + if (peekToken().string == "(") { + do { + nextToken(); + result.parameters.push(parseNumericExpression()); + } while (peekToken().string == ","); + consumeToken(")"); + } + return result; + } + + case "number": + return {evaluate: Basic.Const, value: token.value}; + + case "operator": + switch (token.string) { + case "(": { + let result = parseNumericExpression(); + consumeToken(")"); + return result; + } } + break; + } + throw new Error("At " + token.sourceLineNumber + ": expected identifier, number, or (, but got: " + token.string); + } + + function parseFactor() + { + let primary = parsePrimary(); + + let ok = true; + while (ok) { + switch (peekToken().string) { + case "^": + nextToken(); + primary = {evaluate: Basic.NumberPow, left: primary, right: parsePrimary()}; + break; + default: + ok = false; + break; + } + } + + return primary; + } + + function parseTerm() + { + let factor = parseFactor(); + + let ok = true; + while (ok) { + switch (peekToken().string) { + case "*": + nextToken(); + factor = {evaluate: Basic.NumberMul, left: factor, right: parseFactor()}; + break; + case "/": + nextToken(); + factor = {evaluate: Basic.NumberDiv, left: factor, right: parseFactor()}; + break; + default: + ok = false; + break; + } + } + + return factor; + } + + // Only the leading term in Basic can have a sign. + let negate = false; + switch (peekToken().string) { + case "+": + nextToken(); + break; + case "-": + negate = true; + nextToken() + break; + } + + let term = parseTerm(); + if (negate) + term = {evaluate: Basic.NumberNeg, term: term}; + + let ok = true; + while (ok) { + switch (peekToken().string) { + case "+": + nextToken(); + term = {evaluate: Basic.NumberAdd, left: term, right: parseTerm()}; + break; + case "-": + nextToken(); + term = {evaluate: Basic.NumberSub, left: term, right: parseTerm()}; + break; + default: + ok = false; + break; + } + } + + return term; + } + + function parseConstant() + { + switch (peekToken().string) { + case "+": + nextToken(); + return consumeKind("number").value; + case "-": + nextToken(); + return -consumeKind("number").value; + default: + if (isStringExpression()) + return consumeKind("string").value; + return consumeKind("number").value; + } + } + + function parseStringExpression() + { + let token = nextToken(); + switch (token.kind) { + case "string": + return {evaluate: Basic.Const, value: token.value}; + case "identifier": + consumeToken("$"); + return {evaluate: Basic.StringVar, name: token.string}; + default: + throw new Error("At " + token.sourceLineNumber + ": expected string expression but got " + token.string); + } + } + + function isStringExpression() + { + // A string expression must start with a string variable or a string constant. + let token = nextToken(); + if (token.kind == "string") { + pushToken(token); + return true; + } + if (token.kind == "identifier") { + let result = peekToken().string == "$"; + pushToken(token); + return result; + } + pushToken(token); + return false; + } + + function parseRelationalExpression() + { + if (isStringExpression()) { + let left = parseStringExpression(); + let operator = nextToken(); + let evaluate; + switch (operator.string) { + case "=": + evaluate = Basic.Equals; + break; + case "<>": + evaluate = Basic.NotEquals; + break; + default: + throw new Error("At " + operator.sourceLineNumber + ": expected a string comparison operator but got: " + operator.string); + } + return {evaluate, left, right: parseStringExpression()}; + } + + let left = parseNumericExpression(); + let operator = nextToken(); + let evaluate; + switch (operator.string) { + case "=": + evaluate = Basic.Equals; + break; + case "<>": + evaluate = Basic.NotEquals; + break; + case "<": + evaluate = Basic.LessThan; + break; + case ">": + evaluate = Basic.GreaterThan; + break; + case "<=": + evaluate = Basic.LessEqual; + break; + case ">=": + evaluate = Basic.GreaterEqual; + break; + default: + throw new Error("At " + operator.sourceLineNumber + ": expected a numeric comparison operator but got: " + operator.string); + } + return {evaluate, left, right: parseNumericExpression()}; + } + + function parseNonNegativeInteger() + { + let token = nextToken(); + if (!/^[0-9]+$/.test(token.string)) + throw new Error("At ", token.sourceLineNumber + ": expected a line number but got: " + token.string); + return token.value; + } + + function parseGoToStatement() + { + statement.kind = Basic.GoTo; + statement.target = parseNonNegativeInteger(); + } + + function parseGoSubStatement() + { + statement.kind = Basic.GoSub; + statement.target = parseNonNegativeInteger(); + } + + function parseStatement() + { + let statement = {}; + statement.lineNumber = consumeKind("userLineNumber").userLineNumber; + program.statements.set(statement.lineNumber, statement); + + let command = nextToken(); + statement.sourceLineNumber = command.sourceLineNumber; + switch (command.kind) { + case "keyword": + switch (command.string.toLowerCase()) { + case "def": + statement.process = Basic.Def; + statement.name = consumeKind("identifier"); + statement.parameters = []; + if (peekToken().string == "(") { + do { + nextToken(); + statement.parameters.push(consumeKind("identifier")); + } while (peekToken().string == ","); + } + statement.expression = parseNumericExpression(); + break; + case "let": + statement.process = Basic.Let; + statement.variable = parseVariable(); + consumeToken("="); + if (statement.process == Basic.Let) + statement.expression = parseNumericExpression(); + else + statement.expression = parseStringExpression(); + break; + case "go": { + let next = nextToken(); + if (next.string == "to") + parseGoToStatement(); + else if (next.string == "sub") + parseGoSubStatement(); + else + throw new Error("At " + next.sourceLineNumber + ": expected to or sub but got: " + next.string); + break; + } + case "goto": + parseGoToStatement(); + break; + case "gosub": + parseGoSubStatement(); + break; + case "if": + statement.process = Basic.If; + statement.condition = parseRelationalExpression(); + consumeToken("then"); + statement.target = parseNonNegativeInteger(); + break; + case "return": + statement.process = Basic.Return; + break; + case "stop": + statement.process = Basic.Stop; + break; + case "on": + statement.process = Basic.On; + statement.expression = parseNumericExpression(); + if (peekToken().string == "go") { + consumeToken("go"); + consumeToken("to"); + } else + consumeToken("goto"); + statement.targets = []; + for (;;) { + statement.targets.push(parseNonNegativeInteger()); + if (peekToken().string != ",") + break; + nextToken(); + } + break; + case "for": + statement.process = Basic.For; + statement.variable = consumeKind("identifier").string; + consumeToken("="); + statement.initial = parseNumericExpression(); + consumeToken("to"); + statement.limit = parseNumericExpression(); + if (peekToken().string == "step") { + nextToken(); + statement.step = parseNumericExpression(); + } else + statement.step = {evaluate: Basic.Const, value: 1}; + consumeKind("newLine"); + let lastStatement = parseStatements(); + if (lastStatement.process != Basic.Next) + throw new Error("At " + lastStatement.sourceLineNumber + ": expected next statement"); + if (lastStatement.variable != statement.variable) + throw new Error("At " + lastStatement.sourceLineNumber + ": expected next for " + statement.variable + " but got " + lastStatement.variable); + lastStatement.target = statement; + statement.target = lastStatement; + return statement; + case "next": + statement.process = Basic.Next; + statement.variable = consumeKind("identifier").string; + break; + case "print": { + statement.process = Basic.Print; + statement.items = []; + let ok = true; + while (ok) { + switch (peekToken().string) { + case ",": + nextToken(); + statement.items.push({kind: "comma"}); + break; + case ";": + nextToken(); + break; + case "tab": + nextToken(); + consumeToken("("); + statement.items.push({kind: "tab", value: parseNumericExpression()}); + break; + case "\n": + ok = false; + break; + default: + if (isStringExpression()) { + statement.items.push({kind: "string", value: parseStringExpression()}); + break; + } + statement.items.push({kind: "number", value: parseNumericExpression()}); + break; + } + } + break; + } + case "input": + statement.process = Basic.Input; + statement.items = []; + for (;;) { + stament.items.push(parseVariable()); + if (peekToken().string != ",") + break; + nextToken(); + } + break; + case "read": + statement.process = Basic.Read; + statement.items = []; + for (;;) { + stament.items.push(parseVariable()); + if (peekToken().string != ",") + break; + nextToken(); + } + break; + case "restore": + statement.process = Basic.Restore; + break; + case "data": + for (;;) { + program.data.push(parseConstant()); + if (peekToken().string != ",") + break; + nextToken(); + } + break; + case "dim": + statement.process = Basic.Dim; + statement.items = []; + for (;;) { + let name = consumeKind("identifier").string; + consumeToken("("); + let bounds = []; + bounds.push(parseNonNegativeInteger()); + if (peekToken().string == ",") { + nextToken(); + bounds.push(parseNonNegativeInteger()); + } + consumeToken(")"); + statement.items.push({name, bounds}); + + if (peekToken().string != ",") + break; + consumeToken(","); + } + break; + case "option": { + consumeToken("base"); + let base = parseNonNegativeInteger(); + if (base != 0 && base != 1) + throw new Error("At " + command.sourceLineNumber + ": unexpected base: " + base); + program.base = base; + break; + } + case "randomize": + statement.process = Basic.Randomize; + break; + case "end": + statement.process = Basic.End; + break; + default: + throw new Error("At " + command.sourceLineNumber + ": unexpected command but got: " + command.string); + } + break; + case "remark": + // Just ignore it. + break; + default: + throw new Error("At " + command.sourceLineNumber + ": expected command but got: " + command.string + " (of kind " + command.kind + ")"); + } + + consumeKind("newLine"); + return statement; + } + + function parseStatements() + { + let statement; + do { + statement = parseStatement(); + } while (!statement.process || !statement.process.isBlockEnd); + return statement; + } + + return { + program() + { + program = { + process: Basic.Program, + statements: new Map(), + data: [], + base: 0 + }; + let lastStatement = parseStatements(program.statements); + if (lastStatement.process != Basic.End) + throw new Error("At " + lastStatement.sourceLineNumber + ": expected end"); + + return program; + }, + + statement(program_) + { + program = program_; + return parseStatement(); + } + }; +} + |