matrepl/src/js/parser.js
2021-03-02 14:31:49 +01:00

209 lines
No EOL
5.7 KiB
JavaScript

import {scan, token_types} from './scanner';
export const parse = function (tokens) {
let token_index = 0;
return statement();
function statement() {
if (check(token_types.IDENTIFIER, token_index) && check(token_types.EQUALS, token_index + 1)) {
let var_name = current_token();
advance();
advance();
return {type: 'declaration', var_name: var_name, initializer: expression()};
} else {
return expression();
}
}
function expression() {
return equality();
}
function equality() {
let expr = comparison()
while (match([token_types.EQUALS_EQUALS, token_types.NOT_EQUALS])) {
let operator = previous_token();
let right = unary();
expr = {type: 'binary', left: expr, operator: operator, right: right};
}
return expr;
}
function comparison() {
let expr = addition();
while (match([token_types.LESS, token_types.LESS_OR_EQUAL, token_types.GREATER, token_types.GREATER_OR_EQUAL])) {
let operator = previous_token();
let right = addition();
expr = {type: 'binary', left: expr, operator: operator, right: right};
}
return expr;
}
function addition() {
let expr = multiplication();
while (match([token_types.MINUS, token_types.PLUS])) {
let operator = previous_token();
let right = multiplication();
expr = {type: 'binary', left: expr, operator: operator, right: right};
}
return expr;
}
function multiplication() {
let expr = unary();
while (match([token_types.SLASH, token_types.STAR, token_types.DOT])) {
let operator = previous_token();
let right = unary();
expr = {type: 'binary', left: expr, operator: operator, right: right};
}
return expr;
}
function unary() {
if (match([token_types.NOT, token_types.MINUS])) {
let operator = previous_token();
let right = unary();
return {type: 'unary', operator: operator, right: right};
} else {
return call();
}
}
function call() {
let expr = primary();
for (; ;) {
if (match([token_types.LEFT_PAREN])) {
expr = finish_call(expr.name);
} else {
break;
}
}
return expr;
}
function finish_call(callee) {
let arguments_list = [];
if (!check(token_types.RIGHT_PAREN, token_index)) {
do {
arguments_list.push(expression());
} while (match([token_types.COMMA]));
}
if (!match([token_types.RIGHT_PAREN])) {
throw {message: "Expect ')' after arguments."};
}
return {type: 'call', name: callee, arguments: arguments_list};
}
function primary() {
if (match([token_types.NUMERIC, token_types.STRING])) {
return {type: 'literal', value: previous_token().value, value_type: previous_token().type};
} else if (match([token_types.LAZY])) {
let expression = previous_token().expression;
let tokens = scan(expression);
let parsed_expression = parse(tokens);
return {
type: 'lazy',
expression: expression,
value: parsed_expression
};
} else if (match([token_types.LEFT_PAREN])) {
let expr = expression();
if (expr && match([token_types.RIGHT_PAREN])) {
return {
type: 'group',
expression: expr
};
} else {
throw {message: 'expected expression or )'};
}
} else if (check(token_types.IDENTIFIER, token_index)) {
let identifier = {
type: 'identifier',
name: current_token().value
};
advance();
return identifier;
} else if (check(token_types.AT, token_index)) {
let result = {
type: 'reference',
name: current_token().value
};
advance();
return result;
}
}
/**
* matches token against array of tokens to check for equality (matching type)
* @param tokens_to_match array of tokens
* @returns {boolean}
*/
function match(tokens_to_match) {
for (let i = 0; i < tokens_to_match.length; i++) {
if (are_same(tokens_to_match[i], current_token())) {
advance()
return true;
}
}
return false;
}
/**
* Checks if token at position index matches the given
* @param token_to_check expected token type
* @param index of token to check
* @returns {boolean}
*/
function check(token_to_check, index) {
let token = tokens[index];
if (!token) {
return false;
}
return are_same(token_to_check, token);
}
/**
* checks if 2 tokens have same type
* @param token_1
* @param token_2
* @returns {boolean}
*/
function are_same(token_1, token_2) {
if (is_at_end()) {
return false;
} else {
return token_1.type === token_2.type;
}
}
function is_at_end() {
return token_index >= tokens.length;
}
function advance() {
token_index += 1;
}
function previous_token() {
return tokens[token_index - 1];
}
function current_token() {
return tokens[token_index];
}
}