add: compiler_unit_parser.
All checks were successful
Run Unit Tests / Run-Unit-Tests (push) Successful in -33s
All checks were successful
Run Unit Tests / Run-Unit-Tests (push) Successful in -33s
This commit is contained in:
parent
e278a5dcc6
commit
783c5b3e3e
|
@ -1,3 +1,24 @@
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use nom::branch::alt;
|
||||||
|
use nom::combinator::map;
|
||||||
|
use nom::IResult;
|
||||||
|
use nom::multi::many1;
|
||||||
|
use crate::parser::grammar_parser::{function_declaration_parser, value_declaration_parser};
|
||||||
|
use crate::parser::syntax_tree::SyntaxNode;
|
||||||
|
use crate::tokenizer::LexicalTokenSpan;
|
||||||
|
|
||||||
mod syntax_tree;
|
mod syntax_tree;
|
||||||
mod grammar_parser;
|
mod grammar_parser;
|
||||||
|
|
||||||
|
pub fn compiler_unit_parser(cursor: LexicalTokenSpan) -> IResult<LexicalTokenSpan, Rc<RefCell<SyntaxNode>>> {
|
||||||
|
map(
|
||||||
|
many1(alt((
|
||||||
|
value_declaration_parser,
|
||||||
|
function_declaration_parser
|
||||||
|
))),
|
||||||
|
|blocks| {
|
||||||
|
SyntaxNode::block(&blocks)
|
||||||
|
}
|
||||||
|
)(cursor)
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::parser::syntax_tree::{BinaryNodeType, SyntaxNode, UnaryNodeType};
|
use crate::parser::syntax_tree::{BasicTypes, BinaryNodeType, InitializerNode, SyntaxNode, UnaryNodeType};
|
||||||
use crate::tokenizer::{LexicalTokenSpan, LexicalTokenType};
|
use crate::tokenizer::{LexicalTokenSpan, LexicalTokenType};
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::combinator::{map, opt};
|
use nom::combinator::{map, opt};
|
||||||
use nom::multi::{many0, separated_list0};
|
use nom::multi::{many0, separated_list0, separated_list1};
|
||||||
use nom::sequence::tuple;
|
use nom::sequence::tuple;
|
||||||
use nom::IResult;
|
use nom::IResult;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
@ -510,6 +510,7 @@ fn block_parser(cursor: LexicalTokenSpan) -> IResult<LexicalTokenSpan, Rc<RefCel
|
||||||
tuple((
|
tuple((
|
||||||
lexical!(Delimiter("{")),
|
lexical!(Delimiter("{")),
|
||||||
many0(alt((
|
many0(alt((
|
||||||
|
value_declaration_parser,
|
||||||
statement_parser,
|
statement_parser,
|
||||||
))),
|
))),
|
||||||
lexical!(Delimiter("}"))
|
lexical!(Delimiter("}"))
|
||||||
|
@ -520,6 +521,189 @@ fn block_parser(cursor: LexicalTokenSpan) -> IResult<LexicalTokenSpan, Rc<RefCel
|
||||||
)(cursor)
|
)(cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn value_initializer_parser(cursor: LexicalTokenSpan) -> IResult<LexicalTokenSpan, Rc<RefCell<InitializerNode>>> {
|
||||||
|
alt((
|
||||||
|
map(
|
||||||
|
tuple((
|
||||||
|
lexical!(Delimiter("{")),
|
||||||
|
separated_list0(lexical!(Delimiter(",")), value_initializer_parser),
|
||||||
|
lexical!(Delimiter("}"))
|
||||||
|
)),
|
||||||
|
|(_, initializers, _)| {
|
||||||
|
InitializerNode::array(initializers)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
map(
|
||||||
|
expression_parser,
|
||||||
|
|expression| {
|
||||||
|
InitializerNode::single(expression)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
))(cursor)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ValueDefinitionParseType<'a> = (Rc<RefCell<SyntaxNode>>, Vec<(LexicalTokenSpan<'a>, Rc<RefCell<SyntaxNode>>, LexicalTokenSpan<'a>)>);
|
||||||
|
|
||||||
|
type InitializedValueDefinitionParseType<'a> = (Rc<RefCell<SyntaxNode>>, Vec<(LexicalTokenSpan<'a>, Rc<RefCell<SyntaxNode>>,
|
||||||
|
LexicalTokenSpan<'a>)>, LexicalTokenSpan<'a>, Rc<RefCell<InitializerNode>>);
|
||||||
|
fn value_definition_parser(cursor: LexicalTokenSpan) -> IResult<LexicalTokenSpan, Rc<RefCell<SyntaxNode>>> {
|
||||||
|
alt((
|
||||||
|
map(
|
||||||
|
tuple((
|
||||||
|
identifier_parser,
|
||||||
|
many0(
|
||||||
|
tuple((
|
||||||
|
lexical!(Delimiter("[")),
|
||||||
|
expression_parser,
|
||||||
|
lexical!(Delimiter("]"))
|
||||||
|
))
|
||||||
|
),
|
||||||
|
lexical!(Operator("=")),
|
||||||
|
value_initializer_parser
|
||||||
|
)),
|
||||||
|
|(identifier, indexers, _, initializer): InitializedValueDefinitionParseType| {
|
||||||
|
SyntaxNode::initialized_value_definition(
|
||||||
|
&identifier,
|
||||||
|
indexers.iter().map(|p| &p.1),
|
||||||
|
&initializer,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
map(tuple((
|
||||||
|
identifier_parser,
|
||||||
|
many0(tuple((
|
||||||
|
lexical!(Delimiter("[")),
|
||||||
|
expression_parser,
|
||||||
|
lexical!(Delimiter("]"))
|
||||||
|
)))
|
||||||
|
)),
|
||||||
|
|(identifier, indexers): ValueDefinitionParseType| {
|
||||||
|
SyntaxNode::value_definition(
|
||||||
|
&identifier,
|
||||||
|
indexers.iter().map(|p| &p.1),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
))(cursor)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn basic_type_parser(cursor: LexicalTokenSpan) -> IResult<LexicalTokenSpan, BasicTypes> {
|
||||||
|
alt((
|
||||||
|
map(
|
||||||
|
lexical!(Keyword("void")),
|
||||||
|
|_| {
|
||||||
|
BasicTypes::Void
|
||||||
|
},
|
||||||
|
),
|
||||||
|
map(
|
||||||
|
lexical!(Keyword("int")),
|
||||||
|
|_| {
|
||||||
|
BasicTypes::Integer
|
||||||
|
},
|
||||||
|
),
|
||||||
|
map(
|
||||||
|
lexical!(Keyword("float")),
|
||||||
|
|_| {
|
||||||
|
BasicTypes::Float
|
||||||
|
},
|
||||||
|
)
|
||||||
|
))(cursor)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn value_declaration_parser(cursor: LexicalTokenSpan) -> IResult<LexicalTokenSpan, Rc<RefCell<SyntaxNode>>> {
|
||||||
|
alt((
|
||||||
|
map(
|
||||||
|
tuple((
|
||||||
|
lexical!(Keyword("const")),
|
||||||
|
basic_type_parser,
|
||||||
|
separated_list1(lexical!(Delimiter(",")), value_definition_parser),
|
||||||
|
lexical!(Delimiter(";"))
|
||||||
|
)),
|
||||||
|
|(_, basic_type, definitions, _)| {
|
||||||
|
SyntaxNode::value_declaration(
|
||||||
|
true,
|
||||||
|
basic_type,
|
||||||
|
&definitions,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
map(
|
||||||
|
tuple((
|
||||||
|
basic_type_parser,
|
||||||
|
separated_list1(lexical!(Delimiter(",")), value_definition_parser),
|
||||||
|
lexical!(Delimiter(";"))
|
||||||
|
)),
|
||||||
|
|(basic_type, definitions, _)| {
|
||||||
|
SyntaxNode::value_declaration(
|
||||||
|
false,
|
||||||
|
basic_type,
|
||||||
|
&definitions,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
))(cursor)
|
||||||
|
}
|
||||||
|
|
||||||
|
type FunctionParameterParseResult<'a> = (BasicTypes,
|
||||||
|
Rc<RefCell<SyntaxNode>>,
|
||||||
|
Option<(LexicalTokenSpan<'a>,
|
||||||
|
LexicalTokenSpan<'a>,
|
||||||
|
Vec<(LexicalTokenSpan<'a>, Rc<RefCell<SyntaxNode>>, LexicalTokenSpan<'a>)>)>);
|
||||||
|
fn function_parameter_parser(cursor: LexicalTokenSpan) -> IResult<LexicalTokenSpan, Rc<RefCell<SyntaxNode>>> {
|
||||||
|
map(
|
||||||
|
tuple((
|
||||||
|
basic_type_parser,
|
||||||
|
identifier_parser,
|
||||||
|
opt(tuple((
|
||||||
|
lexical!(Delimiter("[")),
|
||||||
|
lexical!(Delimiter("]")),
|
||||||
|
many0(tuple((
|
||||||
|
lexical!(Delimiter("[")),
|
||||||
|
expression_parser,
|
||||||
|
lexical!(Delimiter("]")),
|
||||||
|
)))
|
||||||
|
)))
|
||||||
|
)),
|
||||||
|
|(basic_type, identifier, indexers): FunctionParameterParseResult| {
|
||||||
|
let parameters = if let Some(indexers) = indexers {
|
||||||
|
let mut result = vec![SyntaxNode::unit()];
|
||||||
|
|
||||||
|
for indexer in indexers.2 {
|
||||||
|
result.push(Rc::clone(&indexer.1));
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
|
||||||
|
SyntaxNode::function_parameter(basic_type, &identifier, ¶meters)
|
||||||
|
},
|
||||||
|
)(cursor)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn function_declaration_parser(cursor: LexicalTokenSpan) -> IResult<LexicalTokenSpan, Rc<RefCell<SyntaxNode>>> {
|
||||||
|
map(
|
||||||
|
tuple((
|
||||||
|
basic_type_parser,
|
||||||
|
identifier_parser,
|
||||||
|
lexical!(Delimiter("(")),
|
||||||
|
separated_list0(lexical!(Delimiter(",")), function_parameter_parser),
|
||||||
|
lexical!(Delimiter(")")),
|
||||||
|
block_parser
|
||||||
|
)),
|
||||||
|
|(basic_type, identifier, _, parameters, _, block)| {
|
||||||
|
SyntaxNode::function_declaration(
|
||||||
|
basic_type,
|
||||||
|
&identifier,
|
||||||
|
¶meters,
|
||||||
|
&block,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)(cursor)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -810,7 +994,7 @@ mod test {
|
||||||
&SyntaxNode::identifier("i".to_owned()), &SyntaxNode::const_integer(1)),
|
&SyntaxNode::identifier("i".to_owned()), &SyntaxNode::const_integer(1)),
|
||||||
&SyntaxNode::assign_statement(&SyntaxNode::identifier("i".to_owned()),
|
&SyntaxNode::assign_statement(&SyntaxNode::identifier("i".to_owned()),
|
||||||
&SyntaxNode::const_integer(2)),
|
&SyntaxNode::const_integer(2)),
|
||||||
&SyntaxNode::unit()
|
&SyntaxNode::unit(),
|
||||||
);
|
);
|
||||||
|
|
||||||
validate_syntax_node("if (i == 1) i = 2;", &node.borrow().node_type, if_statement_parser);
|
validate_syntax_node("if (i == 1) i = 2;", &node.borrow().node_type, if_statement_parser);
|
||||||
|
@ -828,9 +1012,9 @@ mod test {
|
||||||
&SyntaxNode::assign_statement(&SyntaxNode::identifier("i".to_owned()),
|
&SyntaxNode::assign_statement(&SyntaxNode::identifier("i".to_owned()),
|
||||||
&SyntaxNode::const_integer(2)),
|
&SyntaxNode::const_integer(2)),
|
||||||
&SyntaxNode::assign_statement(&SyntaxNode::identifier("i".to_owned()),
|
&SyntaxNode::assign_statement(&SyntaxNode::identifier("i".to_owned()),
|
||||||
&SyntaxNode::const_integer(1))
|
&SyntaxNode::const_integer(1)),
|
||||||
),
|
),
|
||||||
&SyntaxNode::unit()
|
&SyntaxNode::unit(),
|
||||||
);
|
);
|
||||||
|
|
||||||
validate_syntax_node("if (i == 1) if (i == 2) i = 2; else i = 1;", &node.borrow().node_type, if_statement_parser);
|
validate_syntax_node("if (i == 1) if (i == 2) i = 2; else i = 1;", &node.borrow().node_type, if_statement_parser);
|
||||||
|
@ -847,8 +1031,8 @@ mod test {
|
||||||
&SyntaxNode::identifier("i".to_owned()),
|
&SyntaxNode::identifier("i".to_owned()),
|
||||||
&SyntaxNode::binary_node(BinaryNodeType::Add,
|
&SyntaxNode::binary_node(BinaryNodeType::Add,
|
||||||
&SyntaxNode::identifier("i".to_owned()),
|
&SyntaxNode::identifier("i".to_owned()),
|
||||||
&SyntaxNode::const_integer(1))
|
&SyntaxNode::const_integer(1)),
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
validate_syntax_node("while (i != 10 ) i = i + 1;", &node.borrow().node_type, while_statement_parser);
|
validate_syntax_node("while (i != 10 ) i = i + 1;", &node.borrow().node_type, while_statement_parser);
|
||||||
|
@ -889,4 +1073,133 @@ mod test {
|
||||||
validate_syntax_node("return;", &node.borrow().node_type, return_statement_parser);
|
validate_syntax_node("return;", &node.borrow().node_type, return_statement_parser);
|
||||||
validate_syntax_node("return;", &node.borrow().node_type, statement_parser);
|
validate_syntax_node("return;", &node.borrow().node_type, statement_parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn value_initializer_test() {
|
||||||
|
let initializer = InitializerNode::single(SyntaxNode::const_integer(1));
|
||||||
|
let (_, tokens) = lexical_parser("1").unwrap();
|
||||||
|
let (_, node) = value_initializer_parser((&tokens).into()).unwrap();
|
||||||
|
assert_eq!(initializer, node);
|
||||||
|
|
||||||
|
let initializer = InitializerNode::array(vec![InitializerNode::single(SyntaxNode::const_integer(1)),
|
||||||
|
InitializerNode::single(SyntaxNode::const_integer(2))]);
|
||||||
|
let (_, tokens) = lexical_parser("{1, 2}").unwrap();
|
||||||
|
let (_, node) = value_initializer_parser((&tokens).into()).unwrap();
|
||||||
|
assert_eq!(initializer, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn value_definition_test() {
|
||||||
|
let node = SyntaxNode::initialized_value_definition(
|
||||||
|
&SyntaxNode::identifier("i".to_owned()),
|
||||||
|
vec![],
|
||||||
|
&InitializerNode::single(SyntaxNode::const_integer(1)),
|
||||||
|
);
|
||||||
|
validate_syntax_node("i = 1", &node.borrow().node_type, value_definition_parser);
|
||||||
|
|
||||||
|
let node = SyntaxNode::value_definition(
|
||||||
|
&SyntaxNode::identifier("array".to_owned()),
|
||||||
|
vec![
|
||||||
|
SyntaxNode::const_integer(100),
|
||||||
|
SyntaxNode::const_integer(10)
|
||||||
|
].iter(),
|
||||||
|
);
|
||||||
|
validate_syntax_node("array[100][10]", &node.borrow().node_type, value_definition_parser);
|
||||||
|
|
||||||
|
let node = SyntaxNode::initialized_value_definition(
|
||||||
|
&SyntaxNode::identifier("test".to_owned()),
|
||||||
|
vec![
|
||||||
|
SyntaxNode::const_integer(3)
|
||||||
|
].iter(),
|
||||||
|
&InitializerNode::array(vec![
|
||||||
|
InitializerNode::single(SyntaxNode::const_integer(1)),
|
||||||
|
InitializerNode::single(SyntaxNode::const_integer(2)),
|
||||||
|
InitializerNode::single(SyntaxNode::const_integer(3)),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
validate_syntax_node("test[3] = {1,2,3}", &node.borrow().node_type, value_definition_parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn value_declaration_test() {
|
||||||
|
let node1 = SyntaxNode::initialized_value_definition(
|
||||||
|
&SyntaxNode::identifier("i".to_owned()),
|
||||||
|
vec![],
|
||||||
|
&InitializerNode::single(SyntaxNode::const_integer(1)),
|
||||||
|
);
|
||||||
|
let node2 = SyntaxNode::value_definition(
|
||||||
|
&SyntaxNode::identifier("array".to_owned()),
|
||||||
|
vec![
|
||||||
|
SyntaxNode::const_integer(100),
|
||||||
|
SyntaxNode::const_integer(10)
|
||||||
|
].iter(),
|
||||||
|
);
|
||||||
|
let node = SyntaxNode::value_declaration(
|
||||||
|
false,
|
||||||
|
BasicTypes::Integer,
|
||||||
|
&vec![node1, node2],
|
||||||
|
);
|
||||||
|
validate_syntax_node("int i = 1, array[100][10];", &node.borrow().node_type, value_declaration_parser);
|
||||||
|
|
||||||
|
|
||||||
|
let node = SyntaxNode::initialized_value_definition(
|
||||||
|
&SyntaxNode::identifier("test".to_owned()),
|
||||||
|
vec![
|
||||||
|
SyntaxNode::const_integer(3)
|
||||||
|
].iter(),
|
||||||
|
&InitializerNode::array(vec![
|
||||||
|
InitializerNode::single(SyntaxNode::const_float(1f32)),
|
||||||
|
InitializerNode::single(SyntaxNode::const_float(2f32)),
|
||||||
|
InitializerNode::single(SyntaxNode::const_float(3f32)),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
let node = SyntaxNode::value_declaration(
|
||||||
|
true,
|
||||||
|
BasicTypes::Float,
|
||||||
|
&vec![node],
|
||||||
|
);
|
||||||
|
validate_syntax_node("const float test[3] = {1.0,2.0,3.0};", &node.borrow().node_type, value_declaration_parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_parameter_test() {
|
||||||
|
let node = SyntaxNode::function_parameter(BasicTypes::Integer,
|
||||||
|
&SyntaxNode::identifier("a".to_owned()), &vec![]);
|
||||||
|
validate_syntax_node("int a", &node.borrow().node_type, function_parameter_parser);
|
||||||
|
|
||||||
|
let node = SyntaxNode::function_parameter(BasicTypes::Float,
|
||||||
|
&SyntaxNode::identifier("array".to_owned()), &vec![SyntaxNode::unit()]);
|
||||||
|
validate_syntax_node("float array[]", &node.borrow().node_type, function_parameter_parser);
|
||||||
|
|
||||||
|
let node = SyntaxNode::function_parameter(BasicTypes::Integer,
|
||||||
|
&SyntaxNode::identifier("array".to_owned()),
|
||||||
|
&vec![SyntaxNode::unit(), SyntaxNode::const_integer(10)]);
|
||||||
|
validate_syntax_node("int array[][10]", &node.borrow().node_type, function_parameter_parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn function_declaration_test() {
|
||||||
|
let node = SyntaxNode::function_declaration(
|
||||||
|
BasicTypes::Void,
|
||||||
|
&SyntaxNode::identifier("test".to_owned()),
|
||||||
|
&vec![],
|
||||||
|
&SyntaxNode::block(&vec![
|
||||||
|
SyntaxNode::assign_statement(&SyntaxNode::identifier("a".to_owned()), &SyntaxNode::const_integer(1))
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
validate_syntax_node("void test()\
|
||||||
|
{\
|
||||||
|
a = 1;\
|
||||||
|
}", &node.borrow().node_type, function_declaration_parser);
|
||||||
|
|
||||||
|
let node = SyntaxNode::function_declaration(
|
||||||
|
BasicTypes::Integer,
|
||||||
|
&SyntaxNode::identifier("empty".to_owned()),
|
||||||
|
&vec![
|
||||||
|
SyntaxNode::function_parameter(BasicTypes::Integer, &SyntaxNode::identifier("a".to_owned()), &vec![])
|
||||||
|
],
|
||||||
|
&SyntaxNode::block(&vec![])
|
||||||
|
);
|
||||||
|
validate_syntax_node("int empty(int a) {}", &node.borrow().node_type, function_declaration_parser);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use nom::IResult;
|
use crate::parser::syntax_tree::SyntaxNodeType::FunctionParameter;
|
||||||
|
|
||||||
/// 单元表达式类型
|
/// 单元表达式类型
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
@ -50,13 +50,6 @@ pub struct FunctionCallNode {
|
||||||
pub arguments: Vec<Rc<RefCell<SyntaxNode>>>,
|
pub arguments: Vec<Rc<RefCell<SyntaxNode>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub enum BasicTypes {
|
|
||||||
Void,
|
|
||||||
Integer,
|
|
||||||
Float,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct IfStatementNode {
|
pub struct IfStatementNode {
|
||||||
pub condition: Rc<RefCell<SyntaxNode>>,
|
pub condition: Rc<RefCell<SyntaxNode>>,
|
||||||
|
@ -76,6 +69,58 @@ pub struct AssignStatmentNode {
|
||||||
pub expression: Rc<RefCell<SyntaxNode>>,
|
pub expression: Rc<RefCell<SyntaxNode>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum BasicTypes {
|
||||||
|
Void,
|
||||||
|
Integer,
|
||||||
|
Float,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum InitializerNode {
|
||||||
|
Single(Rc<RefCell<SyntaxNode>>),
|
||||||
|
Array(Vec<Rc<RefCell<InitializerNode>>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InitializerNode {
|
||||||
|
pub fn single(node: Rc<RefCell<SyntaxNode>>) -> Rc<RefCell<Self>> {
|
||||||
|
Rc::new(RefCell::new(Self::Single(node)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn array(nodes: Vec<Rc<RefCell<InitializerNode>>>) -> Rc<RefCell<Self>> {
|
||||||
|
Rc::new(RefCell::new(Self::Array(nodes)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct ValueDefinitionNode {
|
||||||
|
pub identifier: Rc<RefCell<SyntaxNode>>,
|
||||||
|
pub indexers: Vec<Rc<RefCell<SyntaxNode>>>,
|
||||||
|
pub initialzier: Option<Rc<RefCell<InitializerNode>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct ValueDeclarationNode {
|
||||||
|
pub is_constant: bool,
|
||||||
|
pub value_type: BasicTypes,
|
||||||
|
pub definitions: Vec<Rc<RefCell<SyntaxNode>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct FunctionParameterNode {
|
||||||
|
pub parameter_type: BasicTypes,
|
||||||
|
pub identifier: Rc<RefCell<SyntaxNode>>,
|
||||||
|
pub indexers: Vec<Rc<RefCell<SyntaxNode>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct FunctionDeclarationNode {
|
||||||
|
pub function_type: BasicTypes,
|
||||||
|
pub identifier: Rc<RefCell<SyntaxNode>>,
|
||||||
|
pub parameters: Vec<Rc<RefCell<SyntaxNode>>>,
|
||||||
|
pub block: Rc<RefCell<SyntaxNode>>,
|
||||||
|
}
|
||||||
|
|
||||||
/// 语法分析树节点类型
|
/// 语法分析树节点类型
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum SyntaxNodeType {
|
pub enum SyntaxNodeType {
|
||||||
|
@ -96,7 +141,10 @@ pub enum SyntaxNodeType {
|
||||||
ExpressionStatement(Rc<RefCell<SyntaxNode>>),
|
ExpressionStatement(Rc<RefCell<SyntaxNode>>),
|
||||||
ReturnStatement(Rc<RefCell<SyntaxNode>>),
|
ReturnStatement(Rc<RefCell<SyntaxNode>>),
|
||||||
Block(Vec<Rc<RefCell<SyntaxNode>>>),
|
Block(Vec<Rc<RefCell<SyntaxNode>>>),
|
||||||
BasicTypeNode(BasicTypes),
|
ValueDefinition(ValueDefinitionNode),
|
||||||
|
ValueDeclaration(ValueDeclarationNode),
|
||||||
|
FunctionParameter(FunctionParameterNode),
|
||||||
|
FunctionDeclaration(FunctionDeclarationNode),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 语法分析器节点
|
/// 语法分析器节点
|
||||||
|
@ -206,7 +254,7 @@ impl SyntaxNode {
|
||||||
node_type: SyntaxNodeType::IfStatement(IfStatementNode {
|
node_type: SyntaxNodeType::IfStatement(IfStatementNode {
|
||||||
condition: Rc::clone(condition),
|
condition: Rc::clone(condition),
|
||||||
if_statement: Rc::clone(if_statement),
|
if_statement: Rc::clone(if_statement),
|
||||||
else_statement: Rc::clone(else_statement)
|
else_statement: Rc::clone(else_statement),
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -215,7 +263,7 @@ impl SyntaxNode {
|
||||||
Rc::new(RefCell::new(Self {
|
Rc::new(RefCell::new(Self {
|
||||||
node_type: SyntaxNodeType::WhileStatement(WhileStatmentNode {
|
node_type: SyntaxNodeType::WhileStatement(WhileStatmentNode {
|
||||||
condition: Rc::clone(condition),
|
condition: Rc::clone(condition),
|
||||||
statement: Rc::clone(statement)
|
statement: Rc::clone(statement),
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -226,5 +274,67 @@ impl SyntaxNode {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn value_definition<'a, T>(identifier: &Rc<RefCell<SyntaxNode>>, indexers: T) -> Rc<RefCell<Self>>
|
||||||
|
where
|
||||||
|
T: IntoIterator<Item=&'a Rc<RefCell<SyntaxNode>>>,
|
||||||
|
{
|
||||||
|
Rc::new(RefCell::new(Self {
|
||||||
|
node_type: SyntaxNodeType::ValueDefinition(ValueDefinitionNode {
|
||||||
|
identifier: Rc::clone(identifier),
|
||||||
|
indexers: indexers.into_iter().map(|x| Rc::clone(x)).collect(),
|
||||||
|
initialzier: None,
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initialized_value_definition<'a, T>(identifier: &Rc<RefCell<SyntaxNode>>,
|
||||||
|
indexers: T,
|
||||||
|
initializer: &Rc<RefCell<InitializerNode>>) -> Rc<RefCell<Self>>
|
||||||
|
where
|
||||||
|
T: IntoIterator<Item=&'a Rc<RefCell<SyntaxNode>>>,
|
||||||
|
{
|
||||||
|
Rc::new(RefCell::new(Self {
|
||||||
|
node_type: SyntaxNodeType::ValueDefinition(ValueDefinitionNode {
|
||||||
|
identifier: Rc::clone(identifier),
|
||||||
|
indexers: indexers.into_iter().map(|x| Rc::clone(x)).collect(),
|
||||||
|
initialzier: Some(Rc::clone(initializer)),
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn value_declaration(is_constant: bool, value_type: BasicTypes, definitions: &Vec<Rc<RefCell<SyntaxNode>>>) -> Rc<RefCell<SyntaxNode>> {
|
||||||
|
Rc::new(RefCell::new(Self {
|
||||||
|
node_type: SyntaxNodeType::ValueDeclaration(ValueDeclarationNode {
|
||||||
|
is_constant,
|
||||||
|
value_type,
|
||||||
|
definitions: definitions.iter().map(|x| Rc::clone(x)).collect(),
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn function_parameter(parameter_type: BasicTypes,
|
||||||
|
identifier: &Rc<RefCell<SyntaxNode>>,
|
||||||
|
indexers: &Vec<Rc<RefCell<SyntaxNode>>>) -> Rc<RefCell<SyntaxNode>> {
|
||||||
|
Rc::new(RefCell::new(Self {
|
||||||
|
node_type: FunctionParameter(FunctionParameterNode {
|
||||||
|
parameter_type,
|
||||||
|
identifier: Rc::clone(identifier),
|
||||||
|
indexers: indexers.iter().map(|x| Rc::clone(x)).collect(),
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn function_declaration(function_type: BasicTypes,
|
||||||
|
identifier: &Rc<RefCell<SyntaxNode>>,
|
||||||
|
parameters: &Vec<Rc<RefCell<SyntaxNode>>>,
|
||||||
|
block: &Rc<RefCell<SyntaxNode>>) -> Rc<RefCell<Self>> {
|
||||||
|
Rc::new(RefCell::new(Self {
|
||||||
|
node_type: SyntaxNodeType::FunctionDeclaration(FunctionDeclarationNode {
|
||||||
|
function_type,
|
||||||
|
identifier: Rc::clone(identifier),
|
||||||
|
parameters: parameters.iter().map(|x| Rc::clone(x)).collect(),
|
||||||
|
block: Rc::clone(block),
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
419
tests/grammar_tests.rs
Normal file
419
tests/grammar_tests.rs
Normal file
|
@ -0,0 +1,419 @@
|
||||||
|
use nom::InputLength;
|
||||||
|
use rustic_sysy::parser::compiler_unit_parser;
|
||||||
|
use rustic_sysy::tokenizer::lexical_parser;
|
||||||
|
|
||||||
|
fn parse_grammar(input: &'static str) {
|
||||||
|
let (_, tokens) = lexical_parser(input).unwrap();
|
||||||
|
let (remaining, node) = compiler_unit_parser((&tokens).into()).unwrap();
|
||||||
|
dbg!(node);
|
||||||
|
|
||||||
|
assert_eq!(0, remaining.input_len());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn main_test() {
|
||||||
|
parse_grammar(r"int main(){
|
||||||
|
return 3;
|
||||||
|
}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn value_definition_test() {
|
||||||
|
parse_grammar(r"//test domain of global var define and local define
|
||||||
|
int a = 3;
|
||||||
|
int b = 5;
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
int a = 5;
|
||||||
|
return a + b;
|
||||||
|
}");
|
||||||
|
|
||||||
|
parse_grammar(r"//test local var define
|
||||||
|
int main(){
|
||||||
|
int a, b0, _c;
|
||||||
|
a = 1;
|
||||||
|
b0 = 2;
|
||||||
|
_c = 3;
|
||||||
|
return b0 + _c;
|
||||||
|
}");
|
||||||
|
|
||||||
|
parse_grammar(r"//test const gloal var define
|
||||||
|
const int a = 10, b = 5;
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
return b;
|
||||||
|
}");
|
||||||
|
|
||||||
|
parse_grammar(r"//test const local var define
|
||||||
|
int main(){
|
||||||
|
const int a = 10, b = 5;
|
||||||
|
return b;
|
||||||
|
}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn array_definition_test() {
|
||||||
|
parse_grammar(r"int a[10][10];
|
||||||
|
int main(){
|
||||||
|
return 0;
|
||||||
|
}");
|
||||||
|
|
||||||
|
parse_grammar(r"//test array define
|
||||||
|
int main(){
|
||||||
|
int a[4][2] = {};
|
||||||
|
int b[4][2] = {1, 2, 3, 4, 5, 6, 7, 8};
|
||||||
|
int c[4][2] = {{1, 2}, {3, 4}, {5, 6}, {7, 8}};
|
||||||
|
int d[4][2] = {1, 2, {3}, {5}, 7 , 8};
|
||||||
|
int e[4][2] = {{d[2][1], c[2][1]}, {3, 4}, {5, 6}, {7, 8}};
|
||||||
|
return e[3][1] + e[0][0] + e[0][1] + a[2][0];
|
||||||
|
}");
|
||||||
|
|
||||||
|
parse_grammar(r"int main(){
|
||||||
|
const int a[4][2] = {{1, 2}, {3, 4}, {}, 7};
|
||||||
|
const int N = 3;
|
||||||
|
int b[4][2] = {};
|
||||||
|
int c[4][2] = {1, 2, 3, 4, 5, 6, 7, 8};
|
||||||
|
int d[N + 1][2] = {1, 2, {3}, {5}, a[3][0], 8};
|
||||||
|
int e[4][2][1] = {{d[2][1], {c[2][1]}}, {3, 4}, {5, 6}, {7, 8}};
|
||||||
|
return e[3][1][0] + e[0][0][0] + e[0][1][0] + d[3][0];
|
||||||
|
}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn operator_priority_test() {
|
||||||
|
parse_grammar(r"//test the priority of add and mul
|
||||||
|
int main(){
|
||||||
|
int a, b, c, d;
|
||||||
|
a = 10;
|
||||||
|
b = 4;
|
||||||
|
c = 2;
|
||||||
|
d = 2;
|
||||||
|
return c + a * b - d;
|
||||||
|
}");
|
||||||
|
|
||||||
|
parse_grammar(r"//test the priority of add and mul
|
||||||
|
int main(){
|
||||||
|
int a, b, c, d;
|
||||||
|
a = 10;
|
||||||
|
b = 4;
|
||||||
|
c = 2;
|
||||||
|
d = 2;
|
||||||
|
return (c + a) * (b - d);
|
||||||
|
}");
|
||||||
|
|
||||||
|
parse_grammar(r"//test the priority of unary operator and binary operator
|
||||||
|
int main(){
|
||||||
|
int a, b;
|
||||||
|
a = 10;
|
||||||
|
b = 30;
|
||||||
|
return a - -5 + b + -5;
|
||||||
|
}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn statement_test() {
|
||||||
|
parse_grammar(r"int k;
|
||||||
|
const int n = 10;
|
||||||
|
int main () {
|
||||||
|
int i = 0;
|
||||||
|
k = 1;
|
||||||
|
while (i <= n - 1) {
|
||||||
|
i = i + 1;
|
||||||
|
k + 1;
|
||||||
|
k = k + k;
|
||||||
|
}
|
||||||
|
putint(k);
|
||||||
|
return k;
|
||||||
|
}");
|
||||||
|
|
||||||
|
parse_grammar(r"// test if-else-if
|
||||||
|
int ifElseIf() {
|
||||||
|
int a;
|
||||||
|
a = 5;
|
||||||
|
int b;
|
||||||
|
b = 10;
|
||||||
|
if(a == 6 || b == 0xb) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (b == 10 && a == 1)
|
||||||
|
a = 25;
|
||||||
|
else if (b == 10 && a == -5)
|
||||||
|
a = a + 15;
|
||||||
|
else
|
||||||
|
a = -+a;
|
||||||
|
}
|
||||||
|
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
putint(ifElseIf());
|
||||||
|
return 0;
|
||||||
|
}");
|
||||||
|
|
||||||
|
parse_grammar(r"// test if-if-else
|
||||||
|
int ififElse() {
|
||||||
|
int a;
|
||||||
|
a = 5;
|
||||||
|
int b;
|
||||||
|
b = 10;
|
||||||
|
if(a == 5)
|
||||||
|
if (b == 10)
|
||||||
|
a = 25;
|
||||||
|
else
|
||||||
|
a = a + 15;
|
||||||
|
|
||||||
|
return (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
return (ififElse());
|
||||||
|
}");
|
||||||
|
|
||||||
|
parse_grammar(r"// test if-{if-else}
|
||||||
|
int if_ifElse_() {
|
||||||
|
int a;
|
||||||
|
a = 5;
|
||||||
|
int b;
|
||||||
|
b = 10;
|
||||||
|
if(a == 5){
|
||||||
|
if (b == 10)
|
||||||
|
a = 25;
|
||||||
|
else
|
||||||
|
a = a + 15;
|
||||||
|
}
|
||||||
|
return (a);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
return (if_ifElse_());
|
||||||
|
}");
|
||||||
|
|
||||||
|
parse_grammar(r"// test while-if
|
||||||
|
int whileIf() {
|
||||||
|
int a;
|
||||||
|
a = 0;
|
||||||
|
int b;
|
||||||
|
b = 0;
|
||||||
|
while (a < 100) {
|
||||||
|
if (a == 5) {
|
||||||
|
b = 25;
|
||||||
|
}
|
||||||
|
else if (a == 10) {
|
||||||
|
b = 42;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
b = a * 2;
|
||||||
|
}
|
||||||
|
a = a + 1;
|
||||||
|
}
|
||||||
|
return (b);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
return (whileIf());
|
||||||
|
}");
|
||||||
|
|
||||||
|
parse_grammar(r"int deepWhileBr(int a, int b) {
|
||||||
|
int c;
|
||||||
|
c = a + b;
|
||||||
|
while (c < 75) {
|
||||||
|
int d;
|
||||||
|
d = 42;
|
||||||
|
if (c < 100) {
|
||||||
|
c = c + d;
|
||||||
|
if (c > 99) {
|
||||||
|
int e;
|
||||||
|
e = d * 2;
|
||||||
|
if (1 == 1) {
|
||||||
|
c = e * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (c);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int p;
|
||||||
|
p = 2;
|
||||||
|
return deepWhileBr(p, p);
|
||||||
|
}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dijkstra_test() {
|
||||||
|
parse_grammar(r"const int INF = 65535;
|
||||||
|
int e[16][16];
|
||||||
|
int book[16];
|
||||||
|
int dis[16];
|
||||||
|
int n, m;
|
||||||
|
int v1, v2, w;
|
||||||
|
|
||||||
|
void Dijkstra()
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
i = 1;
|
||||||
|
while (i <= n) {
|
||||||
|
dis[i] = e[1][i];
|
||||||
|
book[i] = 0;
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
book[1] = 1;
|
||||||
|
|
||||||
|
i = 1;
|
||||||
|
while (i <= n - 1) {
|
||||||
|
int min_num = INF;
|
||||||
|
int min_index = 0;
|
||||||
|
int k = 1;
|
||||||
|
while (k <= n) {
|
||||||
|
if (min_num > dis[k] && book[k] == 0) {
|
||||||
|
min_num = dis[k];
|
||||||
|
min_index = k;
|
||||||
|
}
|
||||||
|
k = k + 1;
|
||||||
|
}
|
||||||
|
book[min_index] = 1;
|
||||||
|
int j = 1;
|
||||||
|
while (j <= n) {
|
||||||
|
if (e[min_index][j] < INF) {
|
||||||
|
if (dis[j] > dis[min_index] + e[min_index][j]) {
|
||||||
|
dis[j] = dis[min_index] + e[min_index][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j = j + 1;
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
n = getint();
|
||||||
|
m = getint();
|
||||||
|
|
||||||
|
i = 1;
|
||||||
|
while (i <= n) {
|
||||||
|
int j = 1;
|
||||||
|
while (j <= n) {
|
||||||
|
if (i == j)
|
||||||
|
e[i][j] = 0;
|
||||||
|
else
|
||||||
|
e[i][j] = INF;
|
||||||
|
j = j + 1;
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 1;
|
||||||
|
while (i <= m) {
|
||||||
|
int u = getint(), v = getint();
|
||||||
|
e[u][v] = getint();
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dijkstra();
|
||||||
|
|
||||||
|
i = 1;
|
||||||
|
while (i <= n) {
|
||||||
|
putint(dis[i]);
|
||||||
|
putch(32);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
putch(10);
|
||||||
|
return 0;
|
||||||
|
}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sort_test() {
|
||||||
|
parse_grammar(r"int n;
|
||||||
|
int bubblesort(int arr[])
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int j;
|
||||||
|
i =0;
|
||||||
|
while(i < n-1){
|
||||||
|
// Last i elements are already in place
|
||||||
|
j = 0;
|
||||||
|
while(j < n-i-1){
|
||||||
|
if (arr[j] > arr[j+1]) {
|
||||||
|
// swap(&arr[j], &arr[j+1]);
|
||||||
|
int tmp;
|
||||||
|
tmp = arr[j+1];
|
||||||
|
arr[j+1] = arr[j];
|
||||||
|
arr[j] = tmp;
|
||||||
|
}
|
||||||
|
j = j + 1;
|
||||||
|
}
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
n = 10;
|
||||||
|
int a[10];
|
||||||
|
a[0]=4;a[1]=3;a[2]=9;a[3]=2;a[4]=0;
|
||||||
|
a[5]=1;a[6]=6;a[7]=5;a[8]=7;a[9]=8;
|
||||||
|
int i;
|
||||||
|
i = bubblesort(a);
|
||||||
|
while (i < n) {
|
||||||
|
int tmp;
|
||||||
|
tmp = a[i];
|
||||||
|
putint(tmp);
|
||||||
|
tmp = 10;
|
||||||
|
putch(tmp);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}");
|
||||||
|
|
||||||
|
parse_grammar(r"int n;
|
||||||
|
int insertsort(int a[])
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
i = 1;
|
||||||
|
while(i<n)
|
||||||
|
{
|
||||||
|
int temp;
|
||||||
|
temp=a[i];
|
||||||
|
int j;
|
||||||
|
j=i-1;
|
||||||
|
while(j>-1&&temp<a[j])
|
||||||
|
{
|
||||||
|
a[j+1]=a[j];
|
||||||
|
j = j - 1;
|
||||||
|
}
|
||||||
|
a[j+1]=temp;
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
n = 10;
|
||||||
|
int a[10];
|
||||||
|
a[0]=4;a[1]=3;a[2]=9;a[3]=2;a[4]=0;
|
||||||
|
a[5]=1;a[6]=6;a[7]=5;a[8]=7;a[9]=8;
|
||||||
|
int i;
|
||||||
|
i = insertsort(a);
|
||||||
|
while (i < n) {
|
||||||
|
int tmp;
|
||||||
|
tmp = a[i];
|
||||||
|
putint(tmp);
|
||||||
|
tmp = 10;
|
||||||
|
putch(tmp);
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user