scan()

in lib/lexer.js [280:532]


  scan() {
    this.skipWhitespaces();
    let start = this.loc();
    if (this.peek === '/') {
      if (this.readch(1) === '/') {
        // consume the //
        this.getch();
        this.getch();
        // comments
        let str = '//';
        while (this.peek !== '\n' && this.peek) {
          str += this.peek;
          this.getch();
        }

        return new Comment(str, {
          start: start,
          end: this.loc()
        });
      }

      if (this.readch(1) === '*') {
        if(this.readch(2) === '*') {
          // consume the /**
          this.getch();
          this.getch();
          this.getch();
          let str = '/**';
          do {
            str += this.peek;
            this.getch();
          } while (!(this.peek === '*' && this.readch(1) === '/')); // ends with '*/'
          // consume */
          str += '*/';
          this.getch();
          this.getch();
          return new Annotation(str, {
            start: start,
            end: this.loc()
          });
        }
        //only /* 
        this.error(`Only '//' or '/**' allowed`);
      }

    }

    if (this.inTemplate) {
      if (this.peek === '}') {
        let str = '';
        this.getch();
        let start = this.loc();
        while (this.peek) {
          if (this.peek === '$') {
            if (this.readch(1) === '{') {
              let end = this.loc();
              // consume '${'
              this.getch();
              this.getch();
              return new TemplateElement(str, false, {
                start, end
              });
            }
          }

          if (this.peek === '`') {
            let end = this.loc();
            this.inTemplate = false;
            this.getch();
            return new TemplateElement(str, true, {
              start, end
            });
          }

          str += this.peek;
          this.getch();
        }

        this.error('Unexpect end of file');
      }
    }

    if (this.peek === '+' && this.readch(1) === '+') {
      // consume '++'
      this.getch();
      this.getch();
      return new OperatorToken(Tag.INCREMENT, '++', {
        start,
        end: this.loc()
      });
    }

    if (this.peek === '-'  && this.readch(1) === '-') {
      // consume '--'
      this.getch();
      this.getch();
      return new OperatorToken(Tag.DECREMENT, '--', {
        start,
        end: this.loc()
      });
    }

    if (this.peek === '=' && this.readch(1) === '=') {
      // consume '=='
      this.getch();
      this.getch();
      return new OperatorToken(Tag.EQ, '==', {
        start,
        end: this.loc()
      });
    }

    if(this.peek === '!' && this.readch(1) === '=') {
      // consume '!='
      this.getch();
      this.getch();
      return new OperatorToken(Tag.NEQ, '!=', {
        start,
        end: this.loc()
      });
    }

    if(this.peek === '>') {
      this.getch();
      if(this.peek === '=') {
        // consume '>='
        this.getch();
        return new OperatorToken(Tag.GTE, '>=', {
          start,
          end: this.loc()
        });
      }
      return new OperatorToken(Tag.GT, '>', {
        start,
        end: this.loc()
      });
    }

    if(this.peek === '<') {
      this.getch();
      if(this.peek === '=') {
        // consume '>='
        this.getch();
        return new OperatorToken(Tag.LTE, '<=', {
          start,
          end: this.loc()
        });
      }
      return new OperatorToken(Tag.LT, '<', {
        start,
        end: this.loc()
      });
    }

    switch (this.peek) {
    case '\'':
    case '"':
      return this.parseString();
    case '`': {
      this.inTemplate = true;
      return this.parseTemplateString();
    }
    }

    // number  = optionalSign decimalLit optionalFraction optionalType
    // optionalFraction = .decimalLit | ε
    // decimalLit = decimalDigit { decimalDigit }
    // optionalType = "L" | "f" | "d" | ε
    // decimalDigit = "0" … "9"
    // optionalSign = "-" | ε
    if (isDecimalDigit(this.peek) || 
    (this.peek === '-' && isDecimalDigit(this.readch(1)))) {
      return this.parseNumber();
    }

    if (isLetter(this.peek) || this.peek === '_' ||
      this.peek === '$') {
      let str = '';
      do {
        if(this.peek === '-' && !isLetter(this.readch(1))) {
          break;
        }
        str += this.peek;
        this.getch();
      } while (isLetter(this.peek) ||
      isDecimalDigit(this.peek) ||
      this.peek === '_' ||
        this.peek === '-');

      // reserve words
      if (this.words.has(str)) {
        var keyword = this.words.get(str);
        return new WordToken(keyword.tag, keyword.lexeme, {
          start: start,
          end: this.loc()
        });
      }

      return new WordToken(Tag.ID, str, {
        start: start,
        end: this.loc()
      });
    }

    if (this.peek === '@') {
      const vid = this.parseVID();
      
      if(this.peek === '('){
        return new WordToken(Tag.NOTE, vid, {
          start,
          end: this.loc()
        });
      }

      return new WordToken(Tag.VID, vid, {
        start,
        end: this.loc()
      });
    }

    if (this.peek === '&') {
      this.getch();
      if (this.peek === '&') {
        this.getch();
        return new OperatorToken(Tag.AND, '&&', {
          start,
          end: this.loc()
        });
      }

      this.error(`Unexpect ${this.peek} after '&', expect '&'`);
    }

    if (this.peek === '|') {
      this.getch();
      if (this.peek === '|') {
        this.getch();
        return new OperatorToken(Tag.OR, '||', {
          start,
          end: this.loc()
        });
      }

      this.error(`Unexpect ${this.peek} after '|', expect '|'`);
    }

    var tok = new Token(this.peek, {
      start,
      end: this.loc()
    });
    this.peek = ' ';
    return tok;
  }