503 lines
14 KiB
Rust
503 lines
14 KiB
Rust
pub mod parsed_statement;
|
|
pub mod parsed_type_modifier;
|
|
#[cfg(test)]
|
|
mod parser_tests;
|
|
|
|
use super::lexer::lex_tokens::{LexToken, TokenType};
|
|
use crate::logger::messages::Message;
|
|
use crate::logger::messages::Message::UnexpectedToken;
|
|
use crate::modifiers::{FieldModifier, TypeModifier};
|
|
use crate::parsing::parser::parsed_type_modifier::ParsedTypeModifier;
|
|
use crate::span::Span;
|
|
use enumflags2::BitFlags;
|
|
use parsed_statement::ParsedStatement;
|
|
|
|
struct ParseReader<'a> {
|
|
tokens: &'a Vec<LexToken>,
|
|
position: usize,
|
|
peek_distance: usize,
|
|
}
|
|
|
|
impl<'a> ParseReader<'a> {
|
|
pub fn peek(&mut self) -> &LexToken {
|
|
let t = self.tokens.get(self.position + self.peek_distance);
|
|
self.peek_distance += 1;
|
|
match t {
|
|
None => self.tokens.last().unwrap(),
|
|
Some(v) => {
|
|
if v.token_type == TokenType::WhiteSpace {
|
|
self.peek()
|
|
} else {
|
|
v
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn next(&mut self) -> &LexToken {
|
|
let t = self.tokens.get(self.position);
|
|
self.position += 1;
|
|
self.peek_distance = 0;
|
|
match t {
|
|
None => self.tokens.last().unwrap(),
|
|
Some(v) => {
|
|
if v.token_type == TokenType::WhiteSpace {
|
|
self.next()
|
|
} else {
|
|
v
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn move_to_peek(&mut self) {
|
|
self.position += self.peek_distance;
|
|
self.peek_distance = 0;
|
|
}
|
|
|
|
pub fn consume(
|
|
&mut self,
|
|
expected: TokenType,
|
|
log: &mut dyn FnMut(Message, Span),
|
|
) -> &LexToken {
|
|
let n = self.next();
|
|
if n.token_type != expected {
|
|
log(
|
|
Message::UnexpectedToken {
|
|
expected: vec![expected],
|
|
found: n.token_type.clone(),
|
|
},
|
|
n.span,
|
|
);
|
|
}
|
|
n
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn reset_peek(&mut self) {
|
|
self.peek_distance = 0;
|
|
}
|
|
|
|
pub fn create_inner(&self) -> ParseReader<'a> {
|
|
ParseReader {
|
|
tokens: self.tokens,
|
|
position: self.position,
|
|
peek_distance: self.peek_distance,
|
|
}
|
|
}
|
|
|
|
pub fn set_from_inner(&mut self, inner: &ParseReader) {
|
|
self.position = inner.position;
|
|
self.peek_distance = inner.peek_distance;
|
|
}
|
|
}
|
|
|
|
pub fn parse(tokens: Vec<LexToken>, log: &mut dyn FnMut(Message, Span)) -> Box<ParsedStatement> {
|
|
assert_ne!(tokens.len(), 0);
|
|
let mut reader = ParseReader {
|
|
tokens: &tokens,
|
|
position: 0,
|
|
peek_distance: 0,
|
|
};
|
|
parse_script(&mut reader, log)
|
|
}
|
|
|
|
fn parse_script(
|
|
reader: &mut ParseReader,
|
|
log: &mut dyn FnMut(Message, Span),
|
|
) -> Box<ParsedStatement> {
|
|
let mut vec: Vec<ParsedStatement> = Vec::new();
|
|
loop {
|
|
let n = reader.peek();
|
|
let token_type = n.token_type.clone();
|
|
let span = n.span;
|
|
match token_type {
|
|
TokenType::NamespaceKeyword => {
|
|
vec.push(*parse_namespace(reader, log));
|
|
}
|
|
TokenType::InterfaceKeyword => vec.push(*parse_interface(reader, log).unwrap()),
|
|
TokenType::EndOfFile => break,
|
|
TokenType::CloseCurlyBracket => break,
|
|
_ => {
|
|
if let Some(s) = parse_interface(reader, log) {
|
|
vec.push(*s);
|
|
} else {
|
|
log(
|
|
UnexpectedToken {
|
|
expected: vec![TokenType::NamespaceKeyword],
|
|
found: token_type,
|
|
},
|
|
span,
|
|
);
|
|
reader.next();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Box::new(ParsedStatement::Script { statements: vec })
|
|
}
|
|
|
|
fn parse_identifier(
|
|
reader: &mut ParseReader,
|
|
log: &mut dyn FnMut(Message, Span),
|
|
) -> Option<String> {
|
|
let identifier_token = reader.next();
|
|
let s: String;
|
|
match &identifier_token.token_type {
|
|
TokenType::Identifier(i) => {
|
|
s = i.to_string();
|
|
}
|
|
_ => {
|
|
log(
|
|
Message::UnexpectedToken {
|
|
found: identifier_token.token_type.clone(),
|
|
expected: vec![TokenType::Identifier(String::new())],
|
|
},
|
|
identifier_token.span,
|
|
);
|
|
return None;
|
|
}
|
|
}
|
|
Some(s)
|
|
}
|
|
|
|
fn parse_namespace(
|
|
reader: &mut ParseReader,
|
|
log: &mut dyn FnMut(Message, Span),
|
|
) -> Box<ParsedStatement> {
|
|
reader.next(); // Consume namespace
|
|
let identifier = parse_identifier(reader, log);
|
|
if identifier.is_none() {
|
|
return Box::new(ParsedStatement::Invalid);
|
|
}
|
|
reader.consume(TokenType::OpenCurlyBracket, log);
|
|
let script = parse_script(reader, log);
|
|
reader.consume(TokenType::CloseCurlyBracket, log);
|
|
Box::new(ParsedStatement::Namespace {
|
|
identifier: identifier.unwrap(),
|
|
script,
|
|
})
|
|
}
|
|
|
|
fn parse_interface(
|
|
reader: &mut ParseReader,
|
|
log: &mut dyn FnMut(Message, Span),
|
|
) -> Option<Box<ParsedStatement>> {
|
|
let mut type_mod: BitFlags<TypeModifier> = BitFlags::empty();
|
|
let identifier: Option<String>;
|
|
let mut has_interface_keyword = false;
|
|
reader.reset_peek();
|
|
loop {
|
|
match &reader.peek().token_type {
|
|
TokenType::ExternalKeyword => type_mod |= TypeModifier::External,
|
|
TokenType::SharedKeyword => type_mod |= TypeModifier::Shared,
|
|
TokenType::Identifier(s) => {
|
|
identifier = Some(s.clone());
|
|
break;
|
|
}
|
|
TokenType::InterfaceKeyword => {
|
|
has_interface_keyword = true;
|
|
}
|
|
_ => {
|
|
return None;
|
|
}
|
|
};
|
|
}
|
|
if !has_interface_keyword {
|
|
return None;
|
|
}
|
|
reader.move_to_peek();
|
|
let mut statements: Vec<ParsedStatement> = Vec::new();
|
|
let mut inherits: Vec<String> = Vec::new();
|
|
loop {
|
|
let t = reader.peek();
|
|
match t.token_type {
|
|
TokenType::Semicolon => {
|
|
reader.next();
|
|
if identifier.is_none() {
|
|
return Some(Box::new(ParsedStatement::Invalid));
|
|
}
|
|
return Some(Box::new(ParsedStatement::Interface {
|
|
type_mod,
|
|
identifier: identifier.unwrap(),
|
|
inherits,
|
|
statements,
|
|
}));
|
|
}
|
|
TokenType::Colon | TokenType::OpenCurlyBracket => break,
|
|
TokenType::EndOfFile => {
|
|
log(
|
|
Message::UnexpectedToken {
|
|
found: t.token_type.clone(),
|
|
expected: vec![
|
|
TokenType::Semicolon,
|
|
TokenType::Colon,
|
|
TokenType::OpenCurlyBracket,
|
|
],
|
|
},
|
|
t.span,
|
|
);
|
|
return Some(Box::new(ParsedStatement::Invalid));
|
|
}
|
|
_ => {
|
|
log(
|
|
Message::UnexpectedToken {
|
|
found: t.token_type.clone(),
|
|
expected: vec![
|
|
TokenType::Semicolon,
|
|
TokenType::Colon,
|
|
TokenType::OpenCurlyBracket,
|
|
],
|
|
},
|
|
t.span,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
reader.reset_peek();
|
|
if reader.peek().token_type == TokenType::Colon {
|
|
reader.next();
|
|
loop {
|
|
let inherit_identifier = parse_identifier(reader, log);
|
|
if inherit_identifier.is_none() {
|
|
continue;
|
|
}
|
|
inherits.push(inherit_identifier.unwrap());
|
|
if reader.peek().token_type != TokenType::Comma {
|
|
break;
|
|
}
|
|
reader.next();
|
|
}
|
|
}
|
|
reader.consume(TokenType::OpenCurlyBracket, log);
|
|
// TODO: parse interfacemethod
|
|
loop {
|
|
if reader.peek().token_type == TokenType::CloseCurlyBracket {
|
|
break;
|
|
}
|
|
let prop = parse_virtprop(reader, log);
|
|
if prop.is_none() {
|
|
break;
|
|
}
|
|
statements.push(*prop.unwrap());
|
|
}
|
|
|
|
reader.consume(TokenType::CloseCurlyBracket, log);
|
|
reader.move_to_peek();
|
|
if identifier.is_none() {
|
|
return Some(Box::new(ParsedStatement::Invalid));
|
|
}
|
|
|
|
Some(Box::new(ParsedStatement::Interface {
|
|
type_mod,
|
|
identifier: identifier.unwrap(),
|
|
inherits,
|
|
statements,
|
|
}))
|
|
}
|
|
|
|
fn parse_statblock(
|
|
_outer_reader: &mut ParseReader,
|
|
_log: &mut dyn FnMut(Message, Span),
|
|
) -> Option<Box<ParsedStatement>> {
|
|
unimplemented!();
|
|
}
|
|
|
|
fn parse_virtprop(
|
|
outer_reader: &mut ParseReader,
|
|
log: &mut dyn FnMut(Message, Span),
|
|
) -> Option<Box<ParsedStatement>> {
|
|
let mut reader = outer_reader.create_inner();
|
|
let mut field_mod: BitFlags<FieldModifier> = BitFlags::empty();
|
|
let property_type: Option<Box<ParsedStatement>>;
|
|
loop {
|
|
let t = reader.peek();
|
|
match t.token_type {
|
|
TokenType::PrivateKeyword => {
|
|
field_mod |= FieldModifier::Private;
|
|
reader.next();
|
|
}
|
|
TokenType::ProtectedKeyword => {
|
|
field_mod |= FieldModifier::Protected;
|
|
reader.next();
|
|
}
|
|
_ => {
|
|
property_type = parse_type(&mut reader, log);
|
|
property_type.as_ref()?;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
let mut is_handle = false;
|
|
if reader.peek().token_type == TokenType::Ampersand {
|
|
reader.next();
|
|
is_handle = true;
|
|
}
|
|
let identifier = parse_identifier(&mut reader, log);
|
|
identifier.as_ref()?;
|
|
let next = reader.next();
|
|
if next.token_type != TokenType::OpenCurlyBracket {
|
|
return None;
|
|
}
|
|
|
|
let mut has_get = false;
|
|
let mut is_get_const = false;
|
|
let mut get_statement: Option<Box<ParsedStatement>> = None;
|
|
|
|
let mut has_set = false;
|
|
let mut is_set_const = false;
|
|
let mut set_statement: Option<Box<ParsedStatement>> = None;
|
|
let start_span = next.span;
|
|
|
|
loop {
|
|
let next = reader.peek();
|
|
match next.token_type {
|
|
TokenType::GetKeyword => {
|
|
reader.next();
|
|
has_get = true;
|
|
let mut peek = reader.peek();
|
|
if peek.token_type == TokenType::ConstKeyword {
|
|
reader.next();
|
|
is_get_const = true;
|
|
peek = reader.peek();
|
|
}
|
|
if peek.token_type != TokenType::Semicolon {
|
|
get_statement = parse_statblock(&mut reader, log);
|
|
} else {
|
|
reader.next();
|
|
}
|
|
}
|
|
TokenType::SetKeyword => {
|
|
reader.next();
|
|
has_set = true;
|
|
let mut peek = reader.peek();
|
|
if peek.token_type == TokenType::ConstKeyword {
|
|
reader.next();
|
|
is_set_const = true;
|
|
peek = reader.peek();
|
|
}
|
|
if peek.token_type != TokenType::Semicolon {
|
|
set_statement = parse_statblock(&mut reader, log);
|
|
} else {
|
|
reader.next();
|
|
}
|
|
}
|
|
_ => break,
|
|
}
|
|
}
|
|
|
|
let next = reader.next();
|
|
if next.token_type != TokenType::CloseCurlyBracket {
|
|
return None;
|
|
}
|
|
if !has_get && !has_set {
|
|
log(
|
|
Message::EmptyProperty,
|
|
Span {
|
|
start: start_span.start,
|
|
end: next.span.end,
|
|
},
|
|
)
|
|
}
|
|
|
|
outer_reader.set_from_inner(&reader);
|
|
Some(Box::new(ParsedStatement::VirtProp {
|
|
field_mod,
|
|
property_type: property_type.unwrap(),
|
|
identifier: identifier.unwrap(),
|
|
is_handle,
|
|
has_get,
|
|
is_get_const,
|
|
get_statement,
|
|
has_set,
|
|
is_set_const,
|
|
set_statement,
|
|
}))
|
|
}
|
|
|
|
fn parse_type(
|
|
reader: &mut ParseReader,
|
|
log: &mut dyn FnMut(Message, Span),
|
|
) -> Option<Box<ParsedStatement>> {
|
|
let f = reader.peek();
|
|
let mut is_const = false;
|
|
if f.token_type == TokenType::ConstKeyword {
|
|
reader.next();
|
|
is_const = true;
|
|
}
|
|
let scope = parse_scope(reader, log);
|
|
let identifier = parse_identifier(reader, log);
|
|
// if none, we already logged an error. Return None.
|
|
identifier.as_ref()?;
|
|
|
|
// TODO: Generics
|
|
|
|
let mut modifiers: Vec<ParsedTypeModifier> = Vec::new();
|
|
loop {
|
|
let n = reader.peek();
|
|
match n.token_type {
|
|
TokenType::OpenBlockBracket => {
|
|
reader.next();
|
|
reader.consume(TokenType::CloseBlockBracket, log);
|
|
modifiers.push(ParsedTypeModifier::Array);
|
|
}
|
|
TokenType::Ampersand => {
|
|
reader.next();
|
|
if reader.peek().token_type == TokenType::ConstKeyword {
|
|
reader.next();
|
|
modifiers.push(ParsedTypeModifier::ConstHandle);
|
|
} else {
|
|
modifiers.push(ParsedTypeModifier::Handle);
|
|
}
|
|
}
|
|
_ => break,
|
|
}
|
|
}
|
|
|
|
Some(Box::new(ParsedStatement::Type {
|
|
is_const,
|
|
scope,
|
|
identifier: identifier.unwrap(),
|
|
modifiers,
|
|
}))
|
|
}
|
|
|
|
fn parse_scope(
|
|
outer_reader: &mut ParseReader,
|
|
_log: &mut dyn FnMut(Message, Span),
|
|
) -> Option<Box<ParsedStatement>> {
|
|
let mut reader = outer_reader.create_inner();
|
|
let is_global = reader.peek().token_type == TokenType::ColonColon;
|
|
if is_global {
|
|
reader.next();
|
|
}
|
|
let mut scope: Vec<String> = Vec::new();
|
|
loop {
|
|
let n = reader.peek();
|
|
if let TokenType::Identifier(s) = &n.token_type {
|
|
let identifier = s.to_string();
|
|
if reader.peek().token_type == TokenType::ColonColon {
|
|
reader.next();
|
|
reader.next();
|
|
scope.push(identifier);
|
|
} else {
|
|
break;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
// TODO: generics
|
|
if !is_global && scope.is_empty() {
|
|
None
|
|
} else {
|
|
outer_reader.set_from_inner(&reader);
|
|
Some(Box::new(ParsedStatement::Scope {
|
|
is_global,
|
|
scope,
|
|
generic_types: None,
|
|
}))
|
|
}
|
|
}
|