use std::borrow::Borrow;
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(PartialOrd, Ord)]
pub enum Type {
I32,
F32,
ID,
Bool,
String,
Resource,
Entity(String),
}
impl Type {
fn from_str<S: Borrow<str>>(s: S) -> Type {
let s = s.borrow();
match s {
"f32" => Type::F32,
"i32" => Type::I32,
"string" => Type::String,
"bool" => Type::Bool,
"id" => Type::ID,
"resource" => Type::Resource,
_ => Type::Entity(s.to_string()),
}
}
pub fn as_str<'a>(&'a self) -> &'a str {
match self {
Type::ID => "id",
Type::Bool => "bool",
Type::String => "string",
Type::I32 => "i32",
Type::F32 => "f32",
Type::Resource => "resource",
Type::Entity(s) => s.as_str(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Variable {
pub name: String,
pub r#type: Type,
}
impl Variable {
pub fn format(&self) -> String {
format!("{}: {}", self.name, self.r#type.as_str())
}
}
type Parameter = Variable;
#[derive(Debug)]
pub struct Function {
pub name: String,
pub params: Vec<Parameter>,
pub ret_type: Option<Type>,
}
impl Function {
pub fn format(&self) -> String {
let mut out = format!("{}(", self.name);
for (idx, param) in self.params.iter().enumerate() {
out.push_str(¶m.name);
out.push_str(": ");
out.push_str(¶m.r#type.as_str());
if idx < self.params.len() - 1 {
out.push_str(", ");
}
}
out.push(')');
if let Some(ret_type) = &self.ret_type {
out.push(' ');
out.push_str(ret_type.as_str());
}
out
}
}
#[derive(Debug)]
pub struct Document {
pub entity_type: String,
pub content: Vec<u8>,
pub tree: tree_sitter::Tree,
pub global_vars: Vec<Variable>,
pub helpers: Vec<Function>,
pub on_functions: Vec<Function>,
}
pub mod parser_utils {
use std::string::FromUtf8Error;
use crate::server::document::{Type, Variable, parser_utils};
pub fn node_get_content<'a>(content: &'a [u8], node: &tree_sitter::Node) -> &'a [u8] {
let range = node.range();
&content[range.start_byte..range.end_byte]
}
#[derive(Debug)]
pub enum ParseVariableDeclarationErr {
#[allow(dead_code)]
StringParseError(FromUtf8Error),
#[allow(dead_code)]
UnsupportedNodeType(&'static str),
}
pub fn parse_variable_declaration(
content: &[u8],
node: &tree_sitter::Node,
) -> Result<Variable, ParseVariableDeclarationErr> {
if node.kind() != "variable_declaration" && node.kind() != "function_parameter" {
return Err(ParseVariableDeclarationErr::UnsupportedNodeType(
node.kind(),
));
}
let name = node.child_by_field_name("name").unwrap();
let kind = node.child_by_field_name("type").unwrap();
let name = parser_utils::node_get_content(&content, &name);
let kind = parser_utils::node_get_content(&content, &kind);
let name = String::from_utf8(name.to_vec())
.map_err(|err| ParseVariableDeclarationErr::StringParseError(err))?;
let kind = String::from_utf8(kind.to_vec())
.map_err(|err| ParseVariableDeclarationErr::StringParseError(err))?;
let kind = Type::from_str(kind);
return Ok(Variable { name, r#type: kind });
}
}
impl Document {
pub fn new(parser: &mut tree_sitter::Parser, content: Vec<u8>, name: String) -> Document {
let tree = parser.parse(&content, None).unwrap();
let mut cursor = tree.root_node().walk();
let global_vars: Vec<Variable> = tree
.root_node()
.children(&mut cursor)
.filter(|child| child.kind() == "variable_declaration")
.map(|child| parser_utils::parse_variable_declaration(&content, &child))
.filter(|var| var.is_ok())
.map(|var| var.unwrap())
.collect();
cursor.reset(tree.root_node());
macro_rules! parse_functions {
($tree:ident, $node_type:expr) => {
$tree
.root_node()
.children(&mut cursor)
.filter(|child| {
if child.kind() == "function_declaration" {
let name = child.child_by_field_name("name").unwrap();
name.kind() == $node_type
} else {
false
}
})
.filter_map(|decl| {
let name = decl.child_by_field_name("name").unwrap();
let ret_type = decl
.child_by_field_name("ret_type")
.map(|ret_type| {
let ret_type = parser_utils::node_get_content(&content, &ret_type);
let ret_type = String::from_utf8(ret_type.to_vec()).ok()?;
let ret_type = match ret_type.as_str() {
"f32" => Type::F32,
"i32" => Type::F32,
"id" => Type::ID,
"string" => Type::String,
_ => {
return None;
}
};
Some(ret_type)
})
.flatten();
let name = parser_utils::node_get_content(&content, &name);
let name = String::from_utf8(name.to_vec()).ok()?;
let mut cursor = decl.walk();
let params: Vec<Parameter> = decl
.children_by_field_name("param", &mut cursor)
.find_map(|param| {
let name = param.child_by_field_name("name").unwrap();
let kind = param.child_by_field_name("type").unwrap();
let name = parser_utils::node_get_content(&content, &name);
let kind = parser_utils::node_get_content(&content, &kind);
let name = String::from_utf8(name.to_vec()).ok()?;
let kind = String::from_utf8(kind.to_vec()).ok()?;
let kind = Type::from_str(kind);
Some(Parameter { name, r#type: kind })
})
.into_iter()
.collect();
Some(Function {
name,
params,
ret_type,
})
})
.collect()
};
}
let entity_type = name
.split('-')
.last()
.unwrap()
.strip_suffix(".grug")
.unwrap();
let helpers: Vec<Function> = parse_functions!(tree, "helper_identifier");
let on_functions: Vec<Function> = parse_functions!(tree, "on_identifier");
drop(cursor);
Document {
content,
tree,
global_vars,
helpers,
on_functions,
entity_type: entity_type.to_string(),
}
}
}