684 lines
18 KiB
Rust
684 lines
18 KiB
Rust
use anyhow::Result;
|
|
use log::{debug, trace};
|
|
use std::{error::Error, fmt::Display, sync::Arc};
|
|
|
|
use crate::{Token, TokenPosition, TokenType};
|
|
|
|
pub type PResult<T> = Result<T, ParserError>;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct ParserPosition {
|
|
path: Arc<String>,
|
|
position: usize,
|
|
token_positions: Vec<TokenPosition>,
|
|
}
|
|
|
|
impl ParserPosition {
|
|
pub fn from_token(token: &Token) -> Self {
|
|
Self {
|
|
path: token.2.path.clone(),
|
|
position: token.2.start,
|
|
token_positions: vec![token.2.clone()],
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait Node {
|
|
fn get_position(&self) -> ParserPosition;
|
|
fn get_name(&self) -> String {
|
|
String::from("")
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum RootNode {
|
|
Define(DefineStatement),
|
|
Import(ImportStatement),
|
|
Service(ServiceStatement),
|
|
Type(TypeStatement),
|
|
Enum(EnumStatement),
|
|
}
|
|
|
|
impl Node for RootNode {
|
|
fn get_position(&self) -> ParserPosition {
|
|
match self {
|
|
RootNode::Define(stmt) => stmt.get_position(),
|
|
RootNode::Import(stmt) => stmt.get_position(),
|
|
RootNode::Service(stmt) => stmt.get_position(),
|
|
RootNode::Type(stmt) => stmt.get_position(),
|
|
RootNode::Enum(stmt) => stmt.get_position(),
|
|
}
|
|
}
|
|
|
|
fn get_name(&self) -> String {
|
|
match self {
|
|
RootNode::Define(_) => String::from("define"),
|
|
RootNode::Import(_) => String::from("import"),
|
|
RootNode::Service(_) => String::from("service"),
|
|
RootNode::Type(_) => String::from("type"),
|
|
RootNode::Enum(_) => String::from("enum"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct DefineStatement {
|
|
pub position: ParserPosition,
|
|
pub key: String,
|
|
pub value: String,
|
|
}
|
|
impl Node for DefineStatement {
|
|
fn get_position(&self) -> ParserPosition {
|
|
self.position.clone()
|
|
}
|
|
fn get_name(&self) -> String {
|
|
format!("define {}", self.key)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct ImportStatement {
|
|
pub position: ParserPosition,
|
|
pub path: String,
|
|
}
|
|
impl Node for ImportStatement {
|
|
fn get_position(&self) -> ParserPosition {
|
|
self.position.clone()
|
|
}
|
|
|
|
fn get_name(&self) -> String {
|
|
format!("import {}", self.path)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct TypeStatement {
|
|
pub position: ParserPosition,
|
|
pub name: String,
|
|
pub fields: Vec<TypeFieldNode>,
|
|
}
|
|
impl Node for TypeStatement {
|
|
fn get_position(&self) -> ParserPosition {
|
|
self.position.clone()
|
|
}
|
|
|
|
fn get_name(&self) -> String {
|
|
format!("type {}", self.name)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct TypeFieldNode {
|
|
pub position: ParserPosition,
|
|
pub name: String,
|
|
pub optional: bool,
|
|
pub fieldtype: String,
|
|
pub array: bool,
|
|
pub map: Option<String>,
|
|
}
|
|
impl Node for TypeFieldNode {
|
|
fn get_position(&self) -> ParserPosition {
|
|
self.position.clone()
|
|
}
|
|
|
|
fn get_name(&self) -> String {
|
|
format!("type_field {}", self.name)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct EnumStatement {
|
|
pub position: ParserPosition,
|
|
pub name: String,
|
|
pub values: Vec<EnumValueNode>,
|
|
}
|
|
impl Node for EnumStatement {
|
|
fn get_position(&self) -> ParserPosition {
|
|
self.position.clone()
|
|
}
|
|
|
|
fn get_name(&self) -> String {
|
|
format!("enum {}", self.name)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct EnumValueNode {
|
|
pub position: ParserPosition,
|
|
pub name: String,
|
|
pub value: Option<i64>,
|
|
}
|
|
impl Node for EnumValueNode {
|
|
fn get_position(&self) -> ParserPosition {
|
|
self.position.clone()
|
|
}
|
|
|
|
fn get_name(&self) -> String {
|
|
format!("enum_field {}", self.name)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct ServiceStatement {
|
|
pub position: ParserPosition,
|
|
pub name: String,
|
|
pub methods: Vec<ServiceMethodNode>,
|
|
}
|
|
impl Node for ServiceStatement {
|
|
fn get_position(&self) -> ParserPosition {
|
|
self.position.clone()
|
|
}
|
|
|
|
fn get_name(&self) -> String {
|
|
format!("service {}", self.name)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct ServiceMethodNode {
|
|
pub position: ParserPosition,
|
|
pub name: String,
|
|
pub inputs: Vec<ServiceMethodInputNode>,
|
|
pub return_type: Option<ServiceMethodReturnNode>,
|
|
pub decorators: Vec<Decorator>,
|
|
}
|
|
impl Node for ServiceMethodNode {
|
|
fn get_position(&self) -> ParserPosition {
|
|
self.position.clone()
|
|
}
|
|
|
|
fn get_name(&self) -> String {
|
|
format!("method {}", self.name)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Decorator {
|
|
pub position: ParserPosition,
|
|
pub name: String,
|
|
pub args: Vec<String>,
|
|
}
|
|
impl Node for Decorator {
|
|
fn get_position(&self) -> ParserPosition {
|
|
self.position.clone()
|
|
}
|
|
|
|
fn get_name(&self) -> String {
|
|
format!("decorator {}", self.name)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct ServiceMethodInputNode {
|
|
pub position: ParserPosition,
|
|
pub name: String,
|
|
pub fieldtype: String,
|
|
pub optional: bool,
|
|
pub array: bool,
|
|
}
|
|
impl Node for ServiceMethodInputNode {
|
|
fn get_position(&self) -> ParserPosition {
|
|
self.position.clone()
|
|
}
|
|
|
|
fn get_name(&self) -> String {
|
|
format!("method_arg {}", self.name)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct ServiceMethodReturnNode {
|
|
pub position: ParserPosition,
|
|
pub fieldtype: String,
|
|
pub array: bool,
|
|
}
|
|
impl Node for ServiceMethodReturnNode {
|
|
fn get_position(&self) -> ParserPosition {
|
|
self.position.clone()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Parser {
|
|
tokens: Vec<Token>,
|
|
position: usize,
|
|
}
|
|
|
|
impl Parser {
|
|
pub fn new(tokens: Vec<Token>) -> Parser {
|
|
// Remove all comments and spaces at this stage
|
|
let tokens = tokens
|
|
.into_iter()
|
|
.filter(|e| e.0 != TokenType::Comment && e.0 != TokenType::Space)
|
|
.collect();
|
|
Parser {
|
|
tokens,
|
|
position: 0,
|
|
}
|
|
}
|
|
|
|
fn has_current_token(&self) -> bool {
|
|
self.position < self.tokens.len()
|
|
}
|
|
|
|
fn current_token(&mut self) -> &Token {
|
|
&self.tokens[self.position]
|
|
}
|
|
|
|
fn assert_types(&mut self, token_types: &[TokenType]) -> PResult<()> {
|
|
if token_types.iter().any(|t| *t == self.current_token().0) {
|
|
Ok(())
|
|
} else {
|
|
Err(ParserError::new(
|
|
&format!("Unexpected token. Expected on of {:?}", token_types),
|
|
self.current_token(),
|
|
))
|
|
}
|
|
}
|
|
|
|
fn eat_token(&mut self) -> PResult<(String, ParserPosition)> {
|
|
debug!("Parser::eat_token()");
|
|
let pos = ParserPosition::from_token(self.current_token());
|
|
let value = self.current_token().1.clone();
|
|
self.position += 1;
|
|
Ok((value, pos))
|
|
}
|
|
|
|
fn eat_token_value(&mut self, value: &str) -> PResult<(String, ParserPosition)> {
|
|
debug!("Parser::eat_token_value({})", value);
|
|
let (val, pos) = self.eat_token()?;
|
|
|
|
if val != value {
|
|
return Err(ParserError::new(
|
|
&format!("Expected token of value {}", value),
|
|
self.current_token(),
|
|
));
|
|
}
|
|
|
|
Ok((val, pos))
|
|
}
|
|
|
|
fn eat_token_type(&mut self, token_type: TokenType) -> PResult<(String, ParserPosition)> {
|
|
debug!("Parser::eat_token_type({:?})", token_type);
|
|
if self.current_token().0 != token_type {
|
|
return Err(ParserError::new(
|
|
&format!("Expected token of type {:?}", token_type),
|
|
self.current_token(),
|
|
));
|
|
}
|
|
|
|
self.eat_token()
|
|
}
|
|
|
|
fn eat_text(&mut self) -> PResult<(String, ParserPosition)> {
|
|
debug!("Parser::eat_text()");
|
|
self.eat_token_type(TokenType::Text)
|
|
}
|
|
|
|
fn eat_text_with_keywords(&mut self) -> PResult<(String, ParserPosition)> {
|
|
debug!("Parser::eat_text_with_keywords()");
|
|
self.assert_types(&[TokenType::Text, TokenType::Keyword])?;
|
|
self.eat_token()
|
|
}
|
|
|
|
fn eat_string_or_text_as_raw(&mut self) -> PResult<String> {
|
|
debug!("Parser::eat_string_or_text_as_raw()");
|
|
self.assert_types(&[TokenType::String, TokenType::Text])?;
|
|
let token = self.current_token();
|
|
if token.0 == TokenType::String {
|
|
let (value, _) = self.eat_token()?;
|
|
let count = value.chars().count();
|
|
Ok(value.chars().skip(1).take(count - 2).collect())
|
|
} else {
|
|
let (value, _) = self.eat_token()?;
|
|
Ok(value)
|
|
}
|
|
}
|
|
|
|
fn eat_number(&mut self) -> PResult<i64> {
|
|
debug!("Parser::eat_number()");
|
|
self.assert_types(&[TokenType::Number])?;
|
|
let (value, _) = self.eat_token()?;
|
|
value
|
|
.parse()
|
|
.map_err(|_| ParserError::new("Invalid number", self.current_token()))
|
|
}
|
|
|
|
fn parse_define(&mut self) -> PResult<DefineStatement> {
|
|
debug!("Parser::parse_define()");
|
|
let (_, position) = self.eat_token_value("define")?;
|
|
|
|
let (key, _) = self.eat_token()?;
|
|
trace!("Parser::parse_define()::key = {}", key);
|
|
|
|
let value = self.eat_string_or_text_as_raw()?;
|
|
|
|
self.eat_token_type(TokenType::Semicolon)?;
|
|
|
|
Ok(DefineStatement {
|
|
position,
|
|
key,
|
|
value,
|
|
})
|
|
}
|
|
|
|
fn parse_import(&mut self) -> PResult<ImportStatement> {
|
|
debug!("Parser::parse_import()");
|
|
let (_, pos) = self.eat_token_value("import")?;
|
|
let path = self.eat_string_or_text_as_raw()?;
|
|
|
|
self.eat_token_type(TokenType::Semicolon)?;
|
|
|
|
Ok(ImportStatement {
|
|
position: pos,
|
|
path,
|
|
})
|
|
}
|
|
|
|
fn parse_enum_value(&mut self) -> PResult<EnumValueNode> {
|
|
debug!("Parser::parse_enum_value()");
|
|
let (name, pos) = self.eat_token()?;
|
|
let value = if self.current_token().0 == TokenType::Equals {
|
|
self.eat_token()?;
|
|
Some(self.eat_number()?)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
Ok(EnumValueNode {
|
|
position: pos,
|
|
name,
|
|
value,
|
|
})
|
|
}
|
|
|
|
fn parse_enum(&mut self) -> PResult<EnumStatement> {
|
|
debug!("Parser::parse_enum()");
|
|
let (_, pos) = self.eat_token_value("enum")?;
|
|
let (name, _) = self.eat_token()?;
|
|
trace!("Parser::parse_enum()::name = {}", name);
|
|
|
|
self.eat_token_type(TokenType::CurlyOpen)?;
|
|
let mut values: Vec<EnumValueNode> = Vec::new();
|
|
|
|
while self.current_token().0 == TokenType::Text {
|
|
values.push(self.parse_enum_value()?);
|
|
if self.current_token().0 == TokenType::Comma {
|
|
//TODO: Maybe use a next flag or break or something with the commas
|
|
self.eat_token()?;
|
|
}
|
|
}
|
|
|
|
self.eat_token_type(TokenType::CurlyClose)?;
|
|
|
|
Ok(EnumStatement {
|
|
position: pos,
|
|
name,
|
|
values,
|
|
})
|
|
}
|
|
|
|
fn parse_type_field(&mut self) -> PResult<TypeFieldNode> {
|
|
debug!("Parser::parse_type_field()");
|
|
let (name, pos) = self.eat_text_with_keywords()?;
|
|
trace!("Parser::parse_type_field()::name = {}", name);
|
|
|
|
let mut optional = false;
|
|
let mut array = false;
|
|
let mut map = None;
|
|
|
|
if self.current_token().0 == TokenType::Questionmark {
|
|
optional = true;
|
|
self.eat_token()?;
|
|
}
|
|
|
|
_ = self.eat_token_type(TokenType::Colon);
|
|
let fieldtype = if self.current_token().0 == TokenType::CurlyOpen {
|
|
self.eat_token()?;
|
|
let (key_type, _) = self.eat_text()?;
|
|
self.eat_token_type(TokenType::Comma)?;
|
|
let (value_type, _) = self.eat_text()?;
|
|
self.eat_token_type(TokenType::CurlyClose)?;
|
|
map = Some(key_type);
|
|
value_type
|
|
} else {
|
|
let (type_name, _) = self.eat_text()?;
|
|
if self.current_token().0 == TokenType::Array {
|
|
array = true;
|
|
self.eat_token()?;
|
|
}
|
|
|
|
type_name
|
|
};
|
|
|
|
self.eat_token_type(TokenType::Semicolon)?;
|
|
|
|
Ok(TypeFieldNode {
|
|
position: pos,
|
|
name,
|
|
optional,
|
|
fieldtype,
|
|
map,
|
|
array,
|
|
})
|
|
}
|
|
|
|
fn parse_type(&mut self) -> PResult<TypeStatement> {
|
|
debug!("Parser::parse_type()");
|
|
let (_, pos) = self.eat_token_value("type")?;
|
|
let (name, _) = self.eat_text()?;
|
|
trace!("Parser::prase_type()::name = {}", name);
|
|
self.eat_token_type(TokenType::CurlyOpen)?;
|
|
|
|
let mut fields = Vec::new();
|
|
while self.current_token().0 == TokenType::Text
|
|
|| self.current_token().0 == TokenType::Keyword
|
|
{
|
|
fields.push(self.parse_type_field()?);
|
|
}
|
|
|
|
self.eat_token_type(TokenType::CurlyClose)?;
|
|
|
|
Ok(TypeStatement {
|
|
position: pos,
|
|
name,
|
|
fields,
|
|
})
|
|
}
|
|
|
|
fn parse_decorator(&mut self) -> PResult<Decorator> {
|
|
debug!("Parser::parse_decorator()");
|
|
let (_, position) = self.eat_token_type(TokenType::At)?;
|
|
let (decorator, _) = self.eat_text()?;
|
|
trace!("Parser::parse_decorator()::name = {}", decorator);
|
|
|
|
self.eat_token_type(TokenType::BracketOpen)?;
|
|
|
|
let mut args = Vec::new();
|
|
let mut first = true;
|
|
while self.current_token().0 != TokenType::BracketClose {
|
|
if first {
|
|
first = false
|
|
} else {
|
|
self.eat_token_type(TokenType::Comma)?;
|
|
}
|
|
|
|
args.push(self.eat_string_or_text_as_raw()?);
|
|
}
|
|
self.eat_token_type(TokenType::BracketClose)?;
|
|
|
|
Ok(Decorator {
|
|
name: decorator,
|
|
args,
|
|
position,
|
|
})
|
|
}
|
|
|
|
fn parse_method(
|
|
&mut self,
|
|
decorators: Vec<Decorator>,
|
|
notification: bool,
|
|
) -> PResult<ServiceMethodNode> {
|
|
debug!(
|
|
"Parser::parse_method({}, {})",
|
|
decorators.len(),
|
|
notification
|
|
);
|
|
let (name, pos) = self.eat_text()?;
|
|
trace!("Parser::parse_method()::name = {}", name);
|
|
|
|
self.eat_token_type(TokenType::BracketOpen)?;
|
|
|
|
let mut inputs = Vec::new();
|
|
|
|
while self.current_token().0 != TokenType::BracketClose {
|
|
let (name, position) = self.eat_text_with_keywords()?;
|
|
|
|
let mut optional = false;
|
|
if self.current_token().0 == TokenType::Questionmark {
|
|
optional = true;
|
|
self.eat_token()?;
|
|
}
|
|
|
|
self.eat_token_type(TokenType::Colon)?;
|
|
let (fieldtype, _) = self.eat_text()?;
|
|
let mut array = false;
|
|
if self.current_token().0 == TokenType::Array {
|
|
array = true;
|
|
self.eat_token()?;
|
|
}
|
|
inputs.push(ServiceMethodInputNode {
|
|
name,
|
|
fieldtype,
|
|
array,
|
|
optional,
|
|
position,
|
|
});
|
|
|
|
if self.current_token().0 == TokenType::Comma {
|
|
self.eat_token()?;
|
|
}
|
|
|
|
trace!(
|
|
"Parser::parse_method()::params_next_token: {}",
|
|
self.current_token().1
|
|
);
|
|
}
|
|
|
|
self.eat_token_type(TokenType::BracketClose)?;
|
|
|
|
let mut return_type = None;
|
|
if !notification {
|
|
self.eat_token_type(TokenType::Colon)?;
|
|
|
|
let (fieldtype, position) = self.eat_text()?;
|
|
let mut array = false;
|
|
if self.current_token().0 == TokenType::Array {
|
|
array = true;
|
|
self.eat_token()?;
|
|
}
|
|
|
|
return_type = Some(ServiceMethodReturnNode {
|
|
position,
|
|
fieldtype,
|
|
array,
|
|
});
|
|
}
|
|
|
|
self.eat_token_type(TokenType::Semicolon)?;
|
|
|
|
Ok(ServiceMethodNode {
|
|
position: pos,
|
|
name,
|
|
inputs,
|
|
return_type,
|
|
decorators,
|
|
})
|
|
}
|
|
|
|
fn parse_service(&mut self) -> PResult<ServiceStatement> {
|
|
debug!("Parser::parse_service()");
|
|
let (_, pos) = self.eat_token_value("service")?;
|
|
let (name, _) = self.eat_text()?;
|
|
trace!("Parser::parse_service()::name = {}", name);
|
|
|
|
self.eat_token_type(TokenType::CurlyOpen)?;
|
|
|
|
let mut methods = Vec::new();
|
|
while self.current_token().0 != TokenType::CurlyClose {
|
|
let mut decorators = Vec::new();
|
|
while self.current_token().0 == TokenType::At {
|
|
decorators.push(self.parse_decorator()?);
|
|
}
|
|
|
|
let mut notification = false;
|
|
if self.current_token().1 == "notification" {
|
|
self.eat_token()?;
|
|
notification = true;
|
|
}
|
|
|
|
methods.push(self.parse_method(decorators, notification)?);
|
|
}
|
|
|
|
self.eat_token_type(TokenType::CurlyClose)?;
|
|
|
|
Ok(ServiceStatement {
|
|
position: pos,
|
|
name,
|
|
methods,
|
|
})
|
|
}
|
|
|
|
fn parse_statement(&mut self) -> PResult<RootNode> {
|
|
debug!("Parser::parse_statement()");
|
|
let token = self.current_token();
|
|
|
|
if let TokenType::Keyword = token.0 {
|
|
trace!("Parser::parse_statement()::type = {}", token.1);
|
|
return match token.1.as_str() {
|
|
"define" => Ok(RootNode::Define(self.parse_define()?)),
|
|
"import" => Ok(RootNode::Import(self.parse_import()?)),
|
|
"type" => Ok(RootNode::Type(self.parse_type()?)),
|
|
"service" => Ok(RootNode::Service(self.parse_service()?)),
|
|
"enum" => Ok(RootNode::Enum(self.parse_enum()?)),
|
|
_ => Err(ParserError::new("Unknown keyword", token)),
|
|
};
|
|
} else {
|
|
Err(ParserError::new("Expected keyword", token))
|
|
}
|
|
}
|
|
|
|
pub fn parse(&mut self) -> PResult<Vec<RootNode>> {
|
|
debug!("Parser::parse()");
|
|
let mut nodes = Vec::new();
|
|
|
|
while self.has_current_token() {
|
|
nodes.push(self.parse_statement()?);
|
|
}
|
|
|
|
Ok(nodes)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct ParserError {
|
|
pub message: String,
|
|
pub token: Token,
|
|
}
|
|
|
|
impl ParserError {
|
|
fn new(msg: &str, token: &Token) -> ParserError {
|
|
ParserError {
|
|
message: format!("{}: {}", msg, token.1),
|
|
token: token.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Error for ParserError {}
|
|
impl Display for ParserError {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "ParserError: {} at {:?}", self.message, self.token)
|
|
}
|
|
}
|