Skip to content
This repository has been archived by the owner on May 19, 2018. It is now read-only.

Type-check UtilParser #485

Merged
merged 1 commit into from Apr 25, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 17 additions & 14 deletions src/parser/util.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { types as tt } from "../tokenizer/types";
// @flow

import { types as tt, type TokenType } from "../tokenizer/types";
import Tokenizer from "../tokenizer";
import type { Node } from "../types";
import { lineBreak } from "../util/whitespace";

// ## Parser utilities

export default class UtilParser extends Tokenizer {
// TODO

addExtra(node, key, val) {
addExtra(node: Node, key: string, val: any): void {
if (!node) return;

const extra = node.extra = node.extra || {};
Expand All @@ -16,13 +19,13 @@ export default class UtilParser extends Tokenizer {

// TODO

isRelational(op) {
isRelational(op: "<" | ">"): boolean {
return this.match(tt.relational) && this.state.value === op;
}

// TODO

expectRelational(op) {
expectRelational(op: "<" | ">"): void {
if (this.isRelational(op)) {
this.next();
} else {
Expand All @@ -32,57 +35,57 @@ export default class UtilParser extends Tokenizer {

// Tests whether parsed token is a contextual keyword.

isContextual(name) {
isContextual(name: string): boolean {
return this.match(tt.name) && this.state.value === name;
}

// Consumes contextual keyword if possible.

eatContextual(name) {
eatContextual(name: string): boolean {
return this.state.value === name && this.eat(tt.name);
}

// Asserts that following token is given contextual keyword.

expectContextual(name, message) {
expectContextual(name: string, message?: string): void {
if (!this.eatContextual(name)) this.unexpected(null, message);
}

// Test whether a semicolon can be inserted at the current position.

canInsertSemicolon() {
canInsertSemicolon(): boolean {
return this.match(tt.eof) ||
this.match(tt.braceR) ||
lineBreak.test(this.input.slice(this.state.lastTokEnd, this.state.start));
}

// TODO

isLineTerminator() {
isLineTerminator(): boolean {
return this.eat(tt.semi) || this.canInsertSemicolon();
}

// Consume a semicolon, or, failing that, see if we are allowed to
// pretend that there is a semicolon at this position.

semicolon() {
semicolon(): void {
Copy link
Member

@xtuc xtuc Apr 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to explicitly write void for the returned type?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without the return type, you should still get errors if there are errors, but they will be more confusing: If you have function f() { return g(); } and call f().toUpperCase(), and there's an error, is it in f, g, or .toUpperCase()?

if (!this.isLineTerminator()) this.unexpected(null, tt.semi);
}

// Expect a token of a given type. If found, consume it, otherwise,
// raise an unexpected token error at given pos.

expect(type, pos) {
return this.eat(type) || this.unexpected(pos, type);
expect(type: TokenType, pos?: ?number): void {
this.eat(type) || this.unexpected(pos, type);
}

// Raise an unexpected token error. Can take the expected token type
// instead of a message string.

unexpected(pos, messageOrType = "Unexpected token") {
unexpected(pos: ?number, messageOrType: string | TokenType = "Unexpected token"): empty {
if (messageOrType && typeof messageOrType === "object" && messageOrType.label) {
messageOrType = `Unexpected token, expected ${messageOrType.label}`;
}
this.raise(pos != null ? pos : this.state.start, messageOrType);
throw this.raise(pos != null ? pos : this.state.start, messageOrType);
}
}