use crate::ast::*; use crate::lexer::Keyword; /** * Copyright 2020 Garrit Franke * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ use crate::lexer::Position; use crate::lexer::{Token, TokenKind}; use crate::parser::infer::infer; use crate::util::string_util::highlight_position_in_file; use std::convert::TryFrom; use std::iter::Peekable; use std::vec::IntoIter; pub struct Parser { pub path: String, tokens: Peekable>, peeked: Vec, current: Option, prev: Option, raw: Option, } impl Parser { pub fn new(tokens: Vec, raw: Option, file_name: String) -> Parser { let tokens_without_whitespace: Vec = tokens .into_iter() .filter(|token| token.kind != TokenKind::Whitespace && token.kind != TokenKind::Comment) .collect(); Parser { path: file_name, tokens: tokens_without_whitespace.into_iter().peekable(), peeked: vec![], current: None, prev: None, raw, } } pub fn parse(&mut self) -> Result { let mut program = self.parse_module()?; // infer types infer(&mut program); Ok(program) } pub(super) fn next(&mut self) -> Result { self.prev = self.current.to_owned(); let item = if self.peeked.is_empty() { self.tokens.next() } else { self.peeked.pop() }; self.current = item.to_owned(); item.ok_or_else(|| "Expected token".into()) } pub(super) fn peek(&mut self) -> Result { let token = self.next()?; self.push(token.to_owned()); Ok(token) } pub(super) fn push(&mut self, token: Token) { self.peeked.push(token); } pub(super) fn has_more(&mut self) -> bool { !self.peeked.is_empty() || self.tokens.peek().is_some() } pub(super) fn match_token(&mut self, token_kind: TokenKind) -> Result { match self.next()? { token if token.kind == token_kind => Ok(token), other => Err(self.make_error(token_kind, other)), } } pub(super) fn peek_token(&mut self, token_kind: TokenKind) -> Result { match self.peek()? { token if token.kind == token_kind => Ok(token), other => Err(format!( "Token {:?} not found, found {:?}", token_kind, other )), } } pub(super) fn match_keyword(&mut self, keyword: Keyword) -> Result<(), String> { let token = self.next()?; match &token.kind { TokenKind::Keyword(ref k) if k == &keyword => Ok(()), _ => Err(self.make_error(TokenKind::SemiColon, token)), } } pub(super) fn match_operator(&mut self) -> Result { BinOp::try_from(self.next()?.kind) } pub(super) fn match_identifier(&mut self) -> Result { match self.next()?.kind { TokenKind::Identifier(n) => Ok(n), other => Err(format!("Expected Identifier, found {:?}", other)), } } pub(super) fn make_error(&mut self, token_kind: TokenKind, other: Token) -> String { match &self.raw { Some(raw_file) => format!( "Token {:?} not found, found {:?}\n{:?}", token_kind, other, highlight_position_in_file(raw_file.to_string(), other.to_owned().pos) ), None => format!("Token {:?} not found, found {:?}", token_kind, other), } } pub(super) fn make_error_msg(&mut self, pos: Position, msg: String) -> String { let pos_string = format!("{}:{}", pos.line, pos.offset); format!("ERROR: {}\nAt {}", msg, pos_string) } pub(super) fn prev(&mut self) -> Option { self.prev.clone() } }