diff --git a/docs/technical/engine/memo.md b/docs/technical/engine/memo.md index 92811d15..8f80c697 100644 --- a/docs/technical/engine/memo.md +++ b/docs/technical/engine/memo.md @@ -64,3 +64,7 @@ HIRノードのIDをシンボルアドレスとして使う。 ### 関数コール 元の環境で実引数を評価 -> 環境を分ける(push frame) -> 仮引数に値をセット + +### 構造体 +構造体は定義してインスタンス化を行ってから使用する。 +インタプリタ実行時、構造体の各インスタンス(`Identifier { fields }`)に対して値が生成される。 diff --git a/uguisu-engine/src/ast.rs b/uguisu-engine/src/ast.rs index 4da96620..9cf3fe49 100644 --- a/uguisu-engine/src/ast.rs +++ b/uguisu-engine/src/ast.rs @@ -5,21 +5,28 @@ pub enum Node { // statement FunctionDeclaration(FunctionDeclaration), VariableDeclaration(VariableDeclaration), + StructDeclaration(StructDeclaration), BreakStatement(BreakStatement), ReturnStatement(ReturnStatement), Assignment(Assignment), IfStatement(IfStatement), LoopStatement(LoopStatement), // expression - Reference(Reference), + Identifier(Identifier), NumberLiteral(NumberLiteral), BoolLiteral(BoolLiteral), StringLiteral(StringLiteral), BinaryExpr(BinaryExpr), UnaryOp(UnaryOp), CallExpr(CallExpr), + StructExpr(StructExpr), + FieldAccess(FieldAccess), // function declaration FuncParam(FuncParam), + // struct declaration + StructDeclField(StructDeclField), + // struct expr + StructExprField(StructExprField), } impl Node { @@ -27,12 +34,14 @@ impl Node { match self { Node::FunctionDeclaration(_) => "FunctionDeclaration", Node::VariableDeclaration(_) => "VariableDeclaration", + Node::StructDeclaration(_) => "StructDeclaration", + Node::StructExpr(_) => "StructExpr", Node::BreakStatement(_) => "BreakStatement", Node::ReturnStatement(_) => "ReturnStatement", Node::Assignment(_) => "Assignment", Node::IfStatement(_) => "IfStatement", Node::LoopStatement(_) => "LoopStatement", - Node::Reference(_) => "Reference", + Node::Identifier(_) => "Identifier", Node::NumberLiteral(_) => "NumberLiteral", Node::BoolLiteral(_) => "BoolLiteral", Node::StringLiteral(_) => "StringLiteral", @@ -40,6 +49,9 @@ impl Node { Node::UnaryOp(_) => "UnaryOp", Node::CallExpr(_) => "CallExpr", Node::FuncParam(_) => "FuncParam", + Node::FieldAccess(_) => "FieldAccess", + Node::StructDeclField(_) => "StructDeclField", + Node::StructExprField(_) => "StructExprField", } } @@ -47,12 +59,14 @@ impl Node { match self { Node::FunctionDeclaration(node) => node.pos, Node::VariableDeclaration(node) => node.pos, + Node::StructDeclaration(node) => node.pos, + Node::StructExpr(node) => node.pos, Node::BreakStatement(node) => node.pos, Node::ReturnStatement(node) => node.pos, Node::Assignment(node) => node.pos, Node::IfStatement(node) => node.pos, Node::LoopStatement(node) => node.pos, - Node::Reference(node) => node.pos, + Node::Identifier(node) => node.pos, Node::NumberLiteral(node) => node.pos, Node::BoolLiteral(node) => node.pos, Node::StringLiteral(node) => node.pos, @@ -60,11 +74,14 @@ impl Node { Node::UnaryOp(node) => node.pos, Node::CallExpr(node) => node.pos, Node::FuncParam(node) => node.pos, + Node::FieldAccess(node) => node.pos, + Node::StructDeclField(node) => node.pos, + Node::StructExprField(node) => node.pos, } } pub fn new_function_declaration( - identifier: String, + name: String, body: Option>, params: Vec, ret: Option, @@ -72,25 +89,25 @@ impl Node { pos: usize, ) -> Self { Node::FunctionDeclaration(FunctionDeclaration { - identifier, + name, body, params, - ret, + ret_type_name: ret, attributes, pos, }) } - pub fn new_func_param(identifier: String, type_identifier: Option, pos: usize) -> Self { + pub fn new_func_param(name: String, type_identifier: Option, pos: usize) -> Self { Node::FuncParam(FuncParam { - identifier, - type_identifier, + name, + type_name: type_identifier, pos, }) } pub fn new_variable_declaration( - identifier: String, + name: String, body: Option, type_identifier: Option, attributes: Vec, @@ -101,14 +118,54 @@ impl Node { None => None, }; Node::VariableDeclaration(VariableDeclaration { - identifier, + name, body, - type_identifier, + type_name: type_identifier, attributes, pos, }) } + pub fn new_struct_declaration( + name: String, + fields: Vec, + pos: usize, + ) -> Self { + Node::StructDeclaration(StructDeclaration { + name, + fields, + pos, + }) + } + + pub fn new_struct_decl_field(name: String, type_identifier: String, pos: usize) -> Self { + Node::StructDeclField(StructDeclField { + name, + type_name: type_identifier, + pos, + }) + } + + pub fn new_struct_expr( + name: String, + fields: Vec, + pos: usize, + ) -> Self { + Node::StructExpr(StructExpr { + name, + fields, + pos, + }) + } + + pub fn new_struct_expr_field(name: String, body: Node, pos: usize) -> Self { + Node::StructExprField(StructExprField { + name, + body: Box::new(body), + pos, + }) + } + pub fn new_break_statement(pos: usize) -> Self { Node::BreakStatement(BreakStatement { pos, @@ -147,9 +204,9 @@ impl Node { Node::LoopStatement(LoopStatement { body, pos }) } - pub fn new_reference(identifier: &str, pos: usize) -> Self { - Node::Reference(Reference { - identifier: identifier.to_string(), + pub fn new_identifier(value: &str, pos: usize) -> Self { + Node::Identifier(Identifier { + name: value.to_string(), pos, }) } @@ -191,6 +248,18 @@ impl Node { }) } + pub fn new_field_access( + name: String, + target: Node, + pos: usize, + ) -> Self { + Node::FieldAccess(FieldAccess { + name, + target: Box::new(target), + pos, + }) + } + pub fn as_func_param(&self) -> &FuncParam { match self { Node::FuncParam(x) => x, @@ -198,20 +267,48 @@ impl Node { } } - pub fn as_reference(&self) -> &Reference { + pub fn as_struct_decl_field(&self) -> &StructDeclField { match self { - Node::Reference(x) => x, - _ => panic!("reference expected"), + Node::StructDeclField(x) => x, + _ => panic!("struct decl field expected"), + } + } + + pub fn as_struct_expr_field(&self) -> &StructExprField { + match self { + Node::StructExprField(x) => x, + _ => panic!("struct expr field expected"), + } + } + + pub fn as_identifier(&self) -> &Identifier { + match self { + Node::Identifier(x) => x, + _ => panic!("identifier expected"), + } + } + + pub fn as_field_access(&self) -> &FieldAccess { + match self { + Node::FieldAccess(x) => x, + _ => panic!("field access expected"), + } + } + + pub fn as_field_access_mut(&mut self) -> &mut FieldAccess { + match self { + Node::FieldAccess(x) => x, + _ => panic!("field access expected"), } } } #[derive(Debug, PartialEq)] pub struct FunctionDeclaration { - pub identifier: String, + pub name: String, pub body: Option>, pub params: Vec, - pub ret: Option, + pub ret_type_name: Option, pub attributes: Vec, pub pos: usize, } @@ -222,20 +319,48 @@ pub enum FunctionAttribute { #[derive(Debug, PartialEq)] pub struct FuncParam { - pub identifier: String, - pub type_identifier: Option, + pub name: String, + pub type_name: Option, pub pos: usize, } #[derive(Debug, PartialEq)] pub struct VariableDeclaration { - pub identifier: String, - pub type_identifier: Option, + pub name: String, + pub type_name: Option, pub attributes: Vec, pub body: Option>, pub pos: usize, } +#[derive(Debug, PartialEq)] +pub struct StructDeclaration { + pub name: String, + pub fields: Vec, // StructDeclField + pub pos: usize, +} + +#[derive(Debug, PartialEq)] +pub struct StructDeclField { + pub name: String, + pub type_name: String, + pub pos: usize, +} + +#[derive(Debug, PartialEq)] +pub struct StructExpr { + pub name: String, + pub fields: Vec, // StructExprField + pub pos: usize, +} + +#[derive(Debug, PartialEq)] +pub struct StructExprField { + pub name: String, + pub body: Box, // expression + pub pos: usize, +} + #[derive(Debug, PartialEq)] pub enum VariableAttribute { Const, @@ -286,8 +411,8 @@ pub struct LoopStatement { } #[derive(Debug, PartialEq)] -pub struct Reference { - pub identifier: String, +pub struct Identifier { + pub name: String, pub pos: usize, } @@ -331,6 +456,13 @@ pub struct CallExpr { pub pos: usize, } +#[derive(Debug, PartialEq)] +pub struct FieldAccess { + pub name: String, + pub target: Box, + pub pos: usize, +} + fn indent(indent: usize) -> String { let mut buf = String::new(); for _ in 0..indent*2 { @@ -351,7 +483,7 @@ fn show_node(node: &Node, source_code: &str, level: usize) { println!("{}{} ({}:{}) {{", indent(level), name, line, column); match node { Node::FunctionDeclaration(node) => { - println!("{}identifier: \"{}\"", indent(level + 1), node.identifier); + println!("{}name: \"{}\"", indent(level + 1), node.name); println!("{}attributes: {{", indent(level + 1)); for attr in node.attributes.iter() { @@ -363,12 +495,12 @@ fn show_node(node: &Node, source_code: &str, level: usize) { show_tree(&node.params, source_code, level + 2); println!("{}}}", indent(level + 1)); - match &node.ret { + match &node.ret_type_name { Some(x) => { - println!("{}ret: \"{}\"", indent(level + 1), x); + println!("{}ret_type_name: \"{}\"", indent(level + 1), x); } None => { - println!("{}ret: (None)", indent(level + 1)); + println!("{}ret_type_name: (None)", indent(level + 1)); } } @@ -384,7 +516,7 @@ fn show_node(node: &Node, source_code: &str, level: usize) { println!("{}}}", indent(level + 1)); } Node::VariableDeclaration(node) => { - println!("{}identifier: \"{}\"", indent(level + 1), node.identifier); + println!("{}name: \"{}\"", indent(level + 1), node.name); println!("{}attributes: {{", indent(level + 1)); for attr in node.attributes.iter() { @@ -392,12 +524,12 @@ fn show_node(node: &Node, source_code: &str, level: usize) { } println!("{}}}", indent(level + 1)); - match &node.type_identifier { + match &node.type_name { Some(x) => { - println!("{}type_identifier: \"{}\"", indent(level + 1), x); + println!("{}type_name: \"{}\"", indent(level + 1), x); } None => { - println!("{}type_identifier: (None)", indent(level + 1)); + println!("{}type_name: (None)", indent(level + 1)); } } @@ -412,6 +544,18 @@ fn show_node(node: &Node, source_code: &str, level: usize) { } println!("{}}}", indent(level + 1)); } + Node::StructDeclaration(node) => { + println!("{}name: \"{}\"", indent(level + 1), node.name); + println!("{}fields: {{", indent(level + 1)); + show_tree(&node.fields, source_code, level + 2); + println!("{}}}", indent(level + 1)); + } + Node::StructExpr(node) => { + println!("{}name: \"{}\"", indent(level + 1), node.name); + println!("{}fields: {{", indent(level + 1)); + show_tree(&node.fields, source_code, level + 2); + println!("{}}}", indent(level + 1)); + } Node::BreakStatement(_) => {} Node::ReturnStatement(node) => { println!("{}body: {{", indent(level + 1)); @@ -467,8 +611,8 @@ fn show_node(node: &Node, source_code: &str, level: usize) { show_tree(&node.body, source_code, level + 2); println!("{}}}", indent(level + 1)); } - Node::Reference(node) => { - println!("{}identifier: \"{}\"", indent(level + 1), node.identifier); + Node::Identifier(node) => { + println!("{}name: \"{}\"", indent(level + 1), node.name); } Node::NumberLiteral(node) => { println!("{}value: {:?}", indent(level + 1), node.value); @@ -507,17 +651,30 @@ fn show_node(node: &Node, source_code: &str, level: usize) { println!("{}}}", indent(level + 1)); } Node::FuncParam(node) => { - println!("{}identifier: \"{}\"", indent(level + 1), node.identifier); + println!("{}name: \"{}\"", indent(level + 1), node.name); - match &node.type_identifier { + match &node.type_name { Some(x) => { - println!("{}type_identifier: \"{}\"", indent(level + 1), x); + println!("{}type_name: \"{}\"", indent(level + 1), x); } None => { - println!("{}type_identifier: (None)", indent(level + 1)); + println!("{}type_name: (None)", indent(level + 1)); } } } + Node::FieldAccess(node) => { + println!("{}name: \"{}\"", indent(level + 1), node.name); + println!("{}target: {{", indent(level + 1)); + show_node(&node.target, source_code, level + 2); + println!("{}}}", indent(level + 1)); + } + Node::StructDeclField(node) => { + println!("{}name: \"{}\"", indent(level + 1), node.name); + println!("{}type_name: \"{}\"", indent(level + 1), node.type_name); + } + Node::StructExprField(node) => { + println!("{}name: \"{}\"", indent(level + 1), node.name); + } } println!("{}}}", indent(level)); } diff --git a/uguisu-engine/src/builtin.rs b/uguisu-engine/src/builtin.rs index 62bf0d96..60b07364 100644 --- a/uguisu-engine/src/builtin.rs +++ b/uguisu-engine/src/builtin.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; use std::time::SystemTime; -use crate::RuntimeError; +use crate::{RuntimeError, hir}; use crate::hir::Type; use crate::hir_run::Value; @@ -24,7 +24,7 @@ impl BuiltinInfo { } } -type BuiltinHandler = fn(&Vec) -> Result; +type BuiltinHandler = fn(&Vec, &BTreeMap) -> Result; pub(crate) struct BuiltinRuntime { table: BTreeMap, @@ -41,10 +41,10 @@ impl BuiltinRuntime { self.table.insert(internal_name.to_owned(), handler); } - pub(crate) fn call(&self, identifier: &str, args: &Vec) -> Result { + pub(crate) fn call(&self, identifier: &str, args: &Vec, node_map: &BTreeMap) -> Result { match self.table.get(identifier) { Some(f) => { - f(args) + f(args, node_map) } None => panic!("unknown builtin function"), } @@ -102,29 +102,29 @@ pub(crate) fn make_infos() -> Vec { pub(crate) fn make_runtime() -> BuiltinRuntime { let mut runtime = BuiltinRuntime::new(); - fn print_str(args: &Vec) -> Result { - let value = args[0].as_string(); // value: string + fn print_str(args: &Vec, node_map: &BTreeMap) -> Result { + let value = args[0].as_string(node_map); // value: string print!("{}", value); Ok(Value::NoneValue) } runtime.add("printStr", print_str); - fn print_num(args: &Vec) -> Result { - let value = args[0].as_number(); // value: number + fn print_num(args: &Vec, node_map: &BTreeMap) -> Result { + let value = args[0].as_number(node_map); // value: number print!("{}", value); Ok(Value::NoneValue) } runtime.add("printNum", print_num); - fn print_lf(_args: &Vec) -> Result { + fn print_lf(_args: &Vec, _node_map: &BTreeMap) -> Result { print!("\n"); Ok(Value::NoneValue) } runtime.add("printLF", print_lf); - fn assert_eq(args: &Vec) -> Result { - let actual = args[0].as_number(); // actual: number - let expected = args[1].as_number(); // expected: number + fn assert_eq(args: &Vec, node_map: &BTreeMap) -> Result { + let actual = args[0].as_number(node_map); // actual: number + let expected = args[1].as_number(node_map); // expected: number if actual != expected { return Err(RuntimeError::new(format!("assertion error. expected `{}`, actual `{}`.", expected, actual).as_str())); } @@ -132,7 +132,7 @@ pub(crate) fn make_runtime() -> BuiltinRuntime { } runtime.add("assertEq", assert_eq); - fn get_unixtime(_args: &Vec) -> Result { + fn get_unixtime(_args: &Vec, _node_map: &BTreeMap) -> Result { let unixtime = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH).map_err(|_e| RuntimeError::new("getUnixtime failed"))? .as_secs(); @@ -145,17 +145,17 @@ pub(crate) fn make_runtime() -> BuiltinRuntime { } runtime.add("getUnixtime", get_unixtime); - fn concat_str(args: &Vec) -> Result { - let x = args[0].as_string(); // x: string - let y = args[1].as_string(); // y: string + fn concat_str(args: &Vec, node_map: &BTreeMap) -> Result { + let x = args[0].as_string(node_map); // x: string + let y = args[1].as_string(node_map); // y: string let mut value = x.to_string(); value += y; Ok(Value::String(value)) } runtime.add("concatStr", concat_str); - fn to_string(args: &Vec) -> Result { - let num = args[0].as_number(); // num: number + fn to_string(args: &Vec, node_map: &BTreeMap) -> Result { + let num = args[0].as_number(node_map); // num: number Ok(Value::String(num.to_string())) } runtime.add("toString", to_string); diff --git a/uguisu-engine/src/hir.rs b/uguisu-engine/src/hir.rs index 8631c0ae..6711751b 100644 --- a/uguisu-engine/src/hir.rs +++ b/uguisu-engine/src/hir.rs @@ -1,5 +1,6 @@ -use std::{collections::BTreeMap, fmt}; use crate::ast; +use std::collections::BTreeMap; +use std::fmt; #[derive(Debug)] pub enum Node { @@ -13,7 +14,7 @@ pub enum Node { // expression Function(Function), Variable(Variable), - Reference(Reference), + Identifier(Identifier), Literal(Literal), RelationalOp(RelationalOp), LogicalBinaryOp(LogicalBinaryOp), @@ -21,6 +22,10 @@ pub enum Node { LogicalUnaryOp(LogicalUnaryOp), CallExpr(CallExpr), FuncParam(FuncParam), + StructExpr(StructExpr), + StructExprField(StructExprField), + StructDeclField(StructDeclField), + FieldAccess(FieldAccess), } impl Node { @@ -34,7 +39,7 @@ impl Node { Node::LoopStatement(_) => "LoopStatement", Node::Function(_) => "Function", Node::Variable(_) => "Variable", - Node::Reference(_) => "Reference", + Node::Identifier(_) => "Identifier", Node::Literal(_) => "Literal", Node::RelationalOp(_) => "RelationalOp", Node::LogicalBinaryOp(_) => "LogicalBinaryOp", @@ -42,15 +47,19 @@ impl Node { Node::LogicalUnaryOp(_) => "LogicalUnaryOp", Node::CallExpr(_) => "CallExpr", Node::FuncParam(_) => "FuncParam", + Node::StructDeclField(_) => "StructDeclField", + Node::StructExpr(_) => "StructExpr", + Node::StructExprField(_) => "StructExprField", + Node::FieldAccess(_) => "FieldAccess", } } pub fn new_declaration( - identifier: String, + name: String, signature: Signature, ) -> Self { Node::Declaration(Declaration { - identifier, + name, signature, }) } @@ -113,8 +122,8 @@ impl Node { }) } - pub fn new_reference(dest: NodeId) -> Self { - Node::Reference(Reference { + pub fn new_identifier(dest: NodeId) -> Self { + Node::Identifier(Identifier { dest, }) } @@ -183,13 +192,36 @@ impl Node { }) } - pub fn new_func_param(identifier: String) -> Self { + pub fn new_func_param(name: String) -> Self { Node::FuncParam(FuncParam { - identifier, + name, // param_index, }) } + pub fn new_struct_expr( + name: String, + fields: BTreeMap, + ) -> Self { + Node::StructExpr(StructExpr { + name, + field_table: fields, + }) + } + + pub fn new_struct_expr_field(body: NodeId) -> Self { + Node::StructExprField(StructExprField { + body, + }) + } + + pub fn new_field_access(name: String, target: NodeId) -> Self { + Node::FieldAccess(FieldAccess { + name, + target, + }) + } + pub fn as_function(&self) -> Result<&Function, String> { match self { Node::Function(x) => Ok(x), @@ -197,6 +229,13 @@ impl Node { } } + pub fn as_variable(&self) -> Result<&Variable, String> { + match self { + Node::Variable(x) => Ok(x), + _ => Err("variable expected".to_owned()), + } + } + pub fn as_decl(&self) -> Result<&Declaration, String> { match self { Node::Declaration(x) => Ok(x), @@ -210,6 +249,27 @@ impl Node { _ => Err("function parameter expected".to_owned()), } } + + pub fn as_struct_decl_field(&self) -> Result<&StructDeclField, String> { + match self { + Node::StructDeclField(x) => Ok(x), + _ => Err("struct declaration field expected".to_owned()), + } + } + + pub fn as_struct_expr(&self) -> Result<&StructExpr, String> { + match self { + Node::StructExpr(x) => Ok(x), + _ => Err("struct expression expected".to_owned()), + } + } + + pub fn as_struct_expr_field(&self) -> Result<&StructExprField, String> { + match self { + Node::StructExprField(x) => Ok(x), + _ => Err("struct expression field expected".to_owned()), + } + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] @@ -225,7 +285,10 @@ impl NodeId { } pub fn get<'a>(&self, node_map: &'a BTreeMap) -> &'a Node { - &node_map[&self] + match node_map.get(self) { + Some(x) => x, + None => panic!("node not found"), + } } } @@ -237,7 +300,7 @@ impl fmt::Display for NodeId { #[derive(Debug)] pub struct Declaration { - pub identifier: String, + pub name: String, pub signature: Signature, //pub ty: Option, } @@ -246,15 +309,30 @@ pub struct Declaration { pub enum Signature { FunctionSignature(FunctionSignature), VariableSignature(VariableSignature), + StructSignature(StructSignature), } impl Signature { + pub fn as_variable_signature(&self) -> Result<&VariableSignature, String> { + match self { + Signature::VariableSignature(x) => Ok(x), + _ => Err("variable signature expected".to_owned()), + } + } + pub fn as_function_signature(&self) -> Result<&FunctionSignature, String> { match self { Signature::FunctionSignature(x) => Ok(x), _ => Err("function signature expected".to_owned()), } } + + pub fn as_struct_signature(&self) -> Result<&StructSignature, String> { + match self { + Signature::StructSignature(x) => Ok(x), + _ => Err("struct signature expected".to_owned()), + } + } } #[derive(Debug)] @@ -269,6 +347,22 @@ pub struct VariableSignature { pub specified_ty: Option, } +#[derive(Debug)] +pub struct StructSignature { + /// StructDeclField + pub field_table: BTreeMap, +} + +#[derive(Debug, Clone)] +pub struct StructDeclField { +} + +#[derive(Debug, Clone)] +pub struct FieldAccess { + pub name: String, + pub target: NodeId, +} + #[derive(Debug)] pub struct BreakStatement { } @@ -281,7 +375,7 @@ pub struct ReturnStatement { #[derive(Debug)] pub struct Assignment { - /// Reference + /// Identifier pub dest: NodeId, /// Expression pub body: NodeId, @@ -314,7 +408,7 @@ pub struct Function { #[derive(Debug, Clone)] pub struct FuncParam { - pub identifier: String, + pub name: String, // pub param_index: usize, } @@ -332,7 +426,7 @@ pub struct Variable { } #[derive(Debug)] -pub struct Reference { +pub struct Identifier { /// Declaration pub dest: NodeId, } @@ -416,12 +510,25 @@ pub enum LogicalUnaryOperator { #[derive(Debug)] pub struct CallExpr { - /// Reference (expects: Reference -> Declaration -> Function) + /// Identifier (expects: Identifier -> Declaration -> Function) pub callee: NodeId, /// Expression pub args: Vec, } +#[derive(Debug, Clone)] +pub struct StructExpr { + pub name: String, + /// StructExprField + pub field_table: BTreeMap, +} + +#[derive(Debug, Clone)] +pub struct StructExprField { + /// expression + pub body: NodeId, +} + pub(crate) fn show_map(node_map: &BTreeMap, symbol_table: &SymbolTable) { for i in 0..node_map.len() { show_node(NodeId::new(i), node_map, symbol_table); @@ -437,7 +544,7 @@ pub(crate) fn show_node(node_id: NodeId, node_map: &BTreeMap, symb } match node { Node::Declaration(decl) => { - println!(" identifier: \"{}\"", decl.identifier); + println!(" name: \"{}\"", decl.name); match &decl.signature { Signature::FunctionSignature(signature) => { println!(" signature(FunctionSignature): {{"); @@ -461,6 +568,15 @@ pub(crate) fn show_node(node_id: NodeId, node_map: &BTreeMap, symb } println!(" }}"); } + Signature::StructSignature(signature) => { + println!(" signature(StructSignature): {{"); + println!(" field_table: {{"); + for (name, node_id) in signature.field_table.iter() { + println!(" \"{}\": [{}]", name, node_id); + } + println!(" }}"); + println!(" }}"); + } } } Node::Function(func) => { @@ -531,7 +647,7 @@ pub(crate) fn show_node(node_id: NodeId, node_map: &BTreeMap, symb } println!(" }}"); } - Node::Reference(x) => { + Node::Identifier(x) => { println!(" dest: {{"); println!(" [{}]", x.dest); println!(" }}"); @@ -583,36 +699,56 @@ pub(crate) fn show_node(node_id: NodeId, node_map: &BTreeMap, symb println!(" }}"); } Node::FuncParam(func_param) => { - println!(" identifier: \"{}\"", func_param.identifier); + println!(" name: \"{}\"", func_param.name); //println!(" type: {:?}", func_param.ty); } + Node::StructDeclField(_) => {} + Node::StructExpr(struct_expr) => { + println!(" name: \"{}\"", struct_expr.name); + println!(" field_table: {{"); + for (name, node_id) in struct_expr.field_table.iter() { + println!(" \"{}\": [{}]", name, node_id); + } + println!(" }}"); + } + Node::StructExprField(field) => { + println!(" body: {{"); + println!(" [{}]", field.body); + println!(" }}"); + } + Node::FieldAccess(node) => { + println!(" name: \"{}\"", node.name); + println!(" target: {{"); + println!(" [{}]", node.target); + println!(" }}"); + } } println!("}}"); } -pub(crate) struct ResolverStack { +pub struct ResolverStack { frames: Vec, trace: bool, } impl ResolverStack { - pub(crate) fn new(trace: bool) -> Self { + pub fn new(trace: bool) -> Self { Self { frames: vec![ResolverFrame::new()], trace, } } - pub(crate) fn is_root_frame(&mut self) -> bool { + pub fn is_root_frame(&mut self) -> bool { self.frames.len() == 1 } - pub(crate) fn push_frame(&mut self) { + pub fn push_frame(&mut self) { if self.trace { println!("push_frame"); } self.frames.insert(0, ResolverFrame::new()); } - pub(crate) fn pop_frame(&mut self) { + pub fn pop_frame(&mut self) { if self.trace { println!("pop_frame"); } if self.is_root_frame() { panic!("Left the root frame."); @@ -620,7 +756,7 @@ impl ResolverStack { self.frames.remove(0); } - pub(crate) fn set_identifier(&mut self, identifier: &str, node_id: NodeId) { + pub fn set_identifier(&mut self, identifier: &str, node_id: NodeId) { if self.trace { println!("set_identifier (identifier: \"{}\", node_id: [{}])", identifier, node_id); } match self.frames.get_mut(0) { Some(frame) => { @@ -630,7 +766,7 @@ impl ResolverStack { } } - pub(crate) fn lookup_identifier(&self, identifier: &str) -> Option { + pub fn lookup_identifier(&self, identifier: &str) -> Option { for frame in self.frames.iter() { match frame.table.get(identifier) { Some(&node_id) => { @@ -679,7 +815,8 @@ impl SymbolTable { let record = SymbolRecord { ty: None, pos: None, - body: None, + decl_body: None, + ident_body: None, }; self.table.insert(node_id, record); } @@ -702,13 +839,22 @@ impl SymbolTable { record.pos = Some(pos); } - pub fn set_body(&mut self, node_id: NodeId, body: NodeId) { - if self.trace { println!("set_body (node_id: [{}], body: [{}])", node_id, body); } + pub fn set_decl_body(&mut self, node_id: NodeId, body: NodeId) { + if self.trace { println!("set_decl_body (node_id: [{}], body: [{}])", node_id, body); } let record = match self.table.get_mut(&node_id) { Some(x) => x, None => panic!("symbol not found"), }; - record.body = Some(body); + record.decl_body = Some(body); + } + + pub fn set_ident_body(&mut self, node_id: NodeId, body: NodeId) { + if self.trace { println!("set_ident_body (node_id: [{}], body: [{}])", node_id, body); } + let record = match self.table.get_mut(&node_id) { + Some(x) => x, + None => panic!("symbol not found"), + }; + record.ident_body = Some(body); } pub fn get(&self, node_id: NodeId) -> &SymbolRecord { @@ -725,7 +871,9 @@ pub struct SymbolRecord { pub ty: Option, pub pos: Option<(usize, usize)>, /// (for Declaration) Variable or Function - pub body: Option, + pub decl_body: Option, + /// (for Identifier) + pub ident_body: Option, } #[derive(Debug, PartialEq, Clone, Copy)] @@ -735,34 +883,55 @@ pub enum Type { Bool, String, Function, + Struct(NodeId), } impl Type { - pub fn get_name(&self) -> &str { + pub fn get_name(&self, node_map: &BTreeMap) -> String { match self { - Type::Void => "void", - Type::Number => "number", - Type::Bool => "bool", - Type::String => "string", - Type::Function => "function", + Type::Void => "void".to_owned(), + Type::Number => "number".to_owned(), + Type::Bool => "bool".to_owned(), + Type::String => "string".to_owned(), + Type::Function => "function".to_owned(), + Type::Struct(node_id) => { + let decl = node_id.get(node_map).as_decl().unwrap(); + let ty_name = String::from("struct ") + &decl.name; + ty_name + } } } - pub fn from_identifier(ty_identifier: &str) -> Result { - match ty_identifier { + pub fn from_name(ty_name: &str, resolver: &ResolverStack, node_map: &BTreeMap) -> Result { + match ty_name { "void" => Err("type `void` is invalid".to_owned()), "number" => Ok(Type::Number), "bool" => Ok(Type::Bool), "string" => Ok(Type::String), - _ => Err("unknown type name".to_owned()), + _ => { + let node_id = match resolver.lookup_identifier(ty_name) { + Some(x) => x, + None => return Err("unknown type name".to_owned()), + }; + let decl = match node_id.get(node_map).as_decl() { + Ok(x) => x, + Err(_) => return Err("unknown type name".to_owned()), + }; + match &decl.signature { + Signature::StructSignature(_) => { + Ok(Type::Struct(node_id)) + } + _ => return Err("unknown type name".to_owned()), + } + } } } - pub fn assert(actual: Type, expected: Type) -> Result { + pub fn assert(actual: Type, expected: Type, node_map: &BTreeMap) -> Result { if actual == expected { Ok(actual) } else { - let message = format!("type mismatched. expected `{}`, found `{}`", expected.get_name(), actual.get_name()); + let message = format!("type mismatched. expected `{}`, found `{}`", expected.get_name(node_map), actual.get_name(node_map)); Err(message) } } diff --git a/uguisu-engine/src/hir_generate.rs b/uguisu-engine/src/hir_generate.rs index 8341a57e..8d0a8a19 100644 --- a/uguisu-engine/src/hir_generate.rs +++ b/uguisu-engine/src/hir_generate.rs @@ -18,6 +18,9 @@ use crate::hir::{ RelationalOperator, ResolverStack, Signature, + StructDeclField, + StructExpr, + StructSignature, SymbolTable, Type, VariableSignature, @@ -65,6 +68,22 @@ impl<'a> HirGenerator<'a> { crate::parse::calc_location(node.get_pos(), self.source_code).map_err(|e| SyntaxError::new(&e)) } + fn get_node_pos(&self, node_id: NodeId) -> Option<(usize, usize)> { + self.symbol_table.get(node_id).pos + } + + fn get_node_ty(&self, node_id: NodeId) -> Option { + self.symbol_table.get(node_id).ty + } + + fn get_decl_body(&self, node_id: NodeId) -> Option { + self.symbol_table.get(node_id).decl_body + } + + fn get_ident_body(&self, node_id: NodeId) -> Option { + self.symbol_table.get(node_id).ident_body + } + fn make_low_error(&self, message: &str, node: &ast::Node) -> SyntaxError { let (line, column) = self.calc_location(node).unwrap(); SyntaxError::new(&format!("{} ({}:{})", message, line, column)) @@ -91,9 +110,9 @@ impl<'a> HirGenerator<'a> { } } - fn resolve_node(&self, node_id: NodeId) -> NodeId { + fn resolve_identifier(&self, node_id: NodeId) -> NodeId { match node_id.get(self.node_map) { - Node::Reference(reference) => self.resolve_node(reference.dest), + Node::Identifier(identifier) => self.resolve_identifier(identifier.dest), _ => node_id, } } @@ -113,9 +132,12 @@ impl<'a> HirGenerator<'a> { Signature::FunctionSignature(_) => { return Err(self.make_low_error("type `function` is not supported", parser_node)); } + Signature::StructSignature(_) => { + return Err(self.make_low_error("struct is not supported", parser_node)); + } }; let ty = match signature.specified_ty { - Some(x) => Type::assert(body_ty, x).map_err(|e| self.make_error(&e, body_id))?, + Some(x) => Type::assert(body_ty, x, self.node_map).map_err(|e| self.make_error(&e, body_id))?, None => body_ty, }; @@ -125,7 +147,7 @@ impl<'a> HirGenerator<'a> { self.symbol_table.set_ty(variable_id, ty); // link declaration - self.symbol_table.set_body(decl_node_id, variable_id); + self.symbol_table.set_decl_body(decl_node_id, variable_id); self.symbol_table.set_ty(decl_node_id, ty); Ok(()) } @@ -153,18 +175,18 @@ impl<'a> HirGenerator<'a> { let decl_node = Node::new_declaration(info.name.clone(), func_signature); let node_id = self.register_node(decl_node); self.symbol_table.set_ty(node_id, Type::Function); - self.symbol_table.set_body(node_id, func_node_id); + self.symbol_table.set_decl_body(node_id, func_node_id); self.resolver.set_identifier(&info.name, node_id); node_id } fn add_call_main(&mut self) -> Result { - // declaration reference of main function + // make identifier of main function let dest_id = match self.resolver.lookup_identifier("main") { Some(x) => x, None => return Err(SyntaxError::new("function `main` is not found")), }; - let callee_node = Node::new_reference(dest_id); + let callee_node = Node::new_identifier(dest_id); let callee_id = self.register_node(callee_node); let dest_ty = self.get_ty_or_err(dest_id)?; self.symbol_table.set_ty(callee_id, dest_ty); @@ -211,13 +233,13 @@ impl<'a> HirGenerator<'a> { for n in func.params.iter() { let param = n.as_func_param(); let func_param = FuncParam { - identifier: param.identifier.clone(), + name: param.name.clone(), // param_index: i, }; func_params.push(func_param); } - let ret_ty = match &func.ret { - Some(x) => Type::from_identifier(x).map_err(|e| self.make_low_error(&e, parser_node))?, // TODO: improve error location + let ret_ty = match &func.ret_type_name { + Some(x) => Type::from_name(x, &self.resolver, self.node_map).map_err(|e| self.make_low_error(&e, parser_node))?, // TODO: improve error location None => Type::Void, }; @@ -229,8 +251,8 @@ impl<'a> HirGenerator<'a> { let node = Node::FuncParam(func_param.clone()); let node_id = self.register_node(node); self.symbol_table.set_pos(node_id, self.calc_location(&func.params[i])?); - let param_type = match &func.params[i].as_func_param().type_identifier { - Some(x) => Type::from_identifier(x).map_err(|e| self.make_error(&e, node_id))?, // TODO: improve error location + let param_type = match &func.params[i].as_func_param().type_name { + Some(x) => Type::from_name(x, &self.resolver, self.node_map).map_err(|e| self.make_error(&e, node_id))?, // TODO: improve error location None => return Err(self.make_error("parameter type missing", node_id)), }; self.symbol_table.set_ty(node_id, param_type); @@ -241,11 +263,11 @@ impl<'a> HirGenerator<'a> { params, ret_ty, }); - let node = Node::new_declaration(func.identifier.clone(), signature); + let node = Node::new_declaration(func.name.clone(), signature); let node_id = self.register_node(node); self.symbol_table.set_pos(node_id, self.calc_location(parser_node)?); self.symbol_table.set_ty(node_id, Type::Function); - self.resolver.set_identifier(&func.identifier, node_id); + self.resolver.set_identifier(&func.name, node_id); node_id }; @@ -271,7 +293,7 @@ impl<'a> HirGenerator<'a> { None => break, }; let param = param_id.get(self.node_map).as_func_param().unwrap(); - self.resolver.set_identifier(¶m.identifier, param_id); + self.resolver.set_identifier(¶m.name, param_id); i += 1; } let func_body = match &func.body { @@ -286,13 +308,13 @@ impl<'a> HirGenerator<'a> { self.symbol_table.set_pos(func_node_id, self.calc_location(parser_node)?); // link declaration - self.symbol_table.set_body(decl_node_id, func_node_id); + self.symbol_table.set_decl_body(decl_node_id, func_node_id); } Ok(decl_node_id) } ast::Node::VariableDeclaration(variable) => { - if self.trace { println!("enter statement (node: {}, identifier: {})", parser_node.get_name(), variable.identifier); } + if self.trace { println!("enter statement (node: {}, name: {})", parser_node.get_name(), variable.name); } let has_const_attr = variable.attributes.iter().any(|x| *x == VariableAttribute::Const); if has_const_attr { @@ -305,8 +327,8 @@ impl<'a> HirGenerator<'a> { // fetch specified type // NOTE: The fact that type `void` cannot be explicitly declared is used to ensure that variables of type `void` are not declared. - let specified_ty = match &variable.type_identifier { - Some(ident) => Some(Type::from_identifier(ident).map_err(|e| self.make_low_error(&e, parser_node))?), // TODO: improve error location + let specified_ty = match &variable.type_name { + Some(ident) => Some(Type::from_name(ident, &self.resolver, self.node_map).map_err(|e| self.make_low_error(&e, parser_node))?), // TODO: improve error location None => None, }; @@ -315,10 +337,10 @@ impl<'a> HirGenerator<'a> { specified_ty, }); // make node - let node = Node::new_declaration(variable.identifier.clone(), signature); + let node = Node::new_declaration(variable.name.clone(), signature); let node_id = self.register_node(node); self.symbol_table.set_pos(node_id, self.calc_location(parser_node)?); - self.resolver.set_identifier(&variable.identifier, node_id); + self.resolver.set_identifier(&variable.name, node_id); if let Some(var_body) = &variable.body { self.define_variable_decl(parser_node, var_body, node_id)?; @@ -326,6 +348,37 @@ impl<'a> HirGenerator<'a> { Ok(node_id) } + ast::Node::StructDeclaration(decl) => { + if self.trace { println!("enter statement (node: {})", parser_node.get_name()); } + + let mut field_table = BTreeMap::new(); + for n in decl.fields.iter() { + let field_node = n.as_struct_decl_field(); + let field_ty = Type::from_name( + &field_node.type_name, + &self.resolver, + self.node_map, + ).map_err(|e| self.make_low_error(&e, parser_node))?; + let node = Node::StructDeclField(StructDeclField {}); + let node_id = self.register_node(node); + self.symbol_table.set_pos(node_id, self.calc_location(n)?); + self.symbol_table.set_ty(node_id, field_ty); + field_table.insert(field_node.name.clone(), node_id); + } + + // make signature + let signature = Signature::StructSignature(StructSignature { + field_table, + }); + // make node + let node = Node::new_declaration(decl.name.clone(), signature); + let node_id = self.register_node(node); + self.symbol_table.set_pos(node_id, self.calc_location(parser_node)?); + self.symbol_table.set_ty(node_id, Type::Struct(node_id)); + self.resolver.set_identifier(&decl.name, node_id); + + Ok(node_id) + } ast::Node::BreakStatement(_) => { if self.trace { println!("enter statement (node: {})", parser_node.get_name()); } if self.resolver.is_root_frame() { @@ -370,19 +423,19 @@ impl<'a> HirGenerator<'a> { return Err(self.make_low_error("An assignment statement cannot be used in global space", parser_node)); } - let reference = statement.dest.as_reference(); - let declaration_id = match self.resolver.lookup_identifier(&reference.identifier) { + let identifier = statement.dest.as_identifier(); + let declaration_id = match self.resolver.lookup_identifier(&identifier.name) { Some(x) => x, None => return Err(self.make_low_error("unknown identifier", parser_node)), }; // if the declaration is not defined, define it. - if let None = self.symbol_table.get(declaration_id).body { + if let None = self.symbol_table.get(declaration_id).decl_body { self.define_variable_decl(parser_node, &statement.body, declaration_id)?; } // make target node - let target_node = Node::new_reference(declaration_id); + let target_node = Node::new_identifier(declaration_id); let target_id = self.register_node(target_node); self.symbol_table.set_pos(target_id, self.calc_location(parser_node)?); let declaration_ty = self.get_ty_or_low_err(declaration_id, parser_node)?; @@ -403,7 +456,7 @@ impl<'a> HirGenerator<'a> { if expr_ty == Type::Void { return Err(self.make_error("A function call that does not return a value cannot be used as an expression.", expr_id)); } - Type::assert(expr_ty, target_ty).map_err(|e| self.make_low_error(&e, parser_node))?; + Type::assert(expr_ty, target_ty, self.node_map).map_err(|e| self.make_low_error(&e, parser_node))?; } AssignmentMode::AddAssign | AssignmentMode::SubAssign @@ -411,9 +464,9 @@ impl<'a> HirGenerator<'a> { | AssignmentMode::DivAssign | AssignmentMode::ModAssign => { let target_ty = self.get_ty_or_low_err(target_id, parser_node)?; - Type::assert(target_ty, Type::Number).map_err(|e| self.make_error(&e, target_id))?; // TODO: improve error message + Type::assert(target_ty, Type::Number, self.node_map).map_err(|e| self.make_error(&e, target_id))?; // TODO: improve error message let expr_ty = self.get_ty_or_err(expr_id)?; - Type::assert(expr_ty, Type::Number).map_err(|e| self.make_error(&e, expr_id))?; + Type::assert(expr_ty, Type::Number, self.node_map).map_err(|e| self.make_error(&e, expr_id))?; } } @@ -436,7 +489,7 @@ impl<'a> HirGenerator<'a> { Some((cond, then_block)) => { let cond_id = analyzer.generate_expr(cond)?; let cond_ty = analyzer.get_ty_or_err(cond_id)?; - Type::assert(cond_ty, Type::Bool).map_err(|e| analyzer.make_error(&e, cond_id))?; + Type::assert(cond_ty, Type::Bool, analyzer.node_map).map_err(|e| analyzer.make_error(&e, cond_id))?; let then_nodes = analyzer.generate_statements(then_block)?; // next else if part let elif = transform(index + 1, analyzer, parser_node, items, else_block)?; @@ -486,13 +539,15 @@ impl<'a> HirGenerator<'a> { self.symbol_table.set_pos(node_id, self.calc_location(parser_node)?); Ok(node_id) } - ast::Node::Reference(_) + ast::Node::Identifier(_) | ast::Node::NumberLiteral(_) | ast::Node::BoolLiteral(_) | ast::Node::StringLiteral(_) | ast::Node::BinaryExpr(_) | ast::Node::UnaryOp(_) - | ast::Node::CallExpr(_) => { + | ast::Node::CallExpr(_) + | ast::Node::FieldAccess(_) + | ast::Node::StructExpr(_) => { if self.trace { println!("enter statement (node: {})", parser_node.get_name()); } // when global scope if self.resolver.is_root_frame() { @@ -505,7 +560,9 @@ impl<'a> HirGenerator<'a> { } Ok(expr_id) } - ast::Node::FuncParam(_) => { + ast::Node::FuncParam(_) + | ast::Node::StructDeclField(_) + | ast::Node::StructExprField(_) => { if self.trace { println!("enter statement (node: {})", parser_node.get_name()); } panic!("unexpected node"); } @@ -520,14 +577,14 @@ impl<'a> HirGenerator<'a> { /// - generate syntax errors fn generate_expr(&mut self, parser_node: &ast::Node) -> Result { let result = match parser_node { - ast::Node::Reference(reference) => { - if self.trace { println!("enter expr (node: {}, identifier: {})", parser_node.get_name(), reference.identifier); } - let dest_id = match self.resolver.lookup_identifier(&reference.identifier) { + ast::Node::Identifier(identifier) => { + if self.trace { println!("enter expr (node: {}, name: {})", parser_node.get_name(), identifier.name); } + let dest_id = match self.resolver.lookup_identifier(&identifier.name) { Some(x) => x, None => return Err(self.make_low_error("unknown identifier", parser_node)), }; - let node = Node::new_reference(dest_id); + let node = Node::new_identifier(dest_id); let node_id = self.register_node(node); let dest_ty = self.get_ty_or_low_err(dest_id, parser_node)?; self.symbol_table.set_pos(node_id, self.calc_location(parser_node)?); @@ -573,7 +630,7 @@ impl<'a> HirGenerator<'a> { "!" => LogicalUnaryOperator::Not, _ => return Err(self.make_low_error("unexpected operation", parser_node)), }; - Type::assert(expr_ty, Type::Bool).map_err(|e| self.make_error(&e, expr_id))?; + Type::assert(expr_ty, Type::Bool, self.node_map).map_err(|e| self.make_error(&e, expr_id))?; let node = Node::new_logical_unary_op(op, expr_id); let node_id = self.register_node(node); self.symbol_table.set_pos(node_id, self.calc_location(parser_node)?); @@ -610,8 +667,8 @@ impl<'a> HirGenerator<'a> { _ => None, }; if let Some(op) = op { - Type::assert(left_ty, Type::Number).map_err(|e| self.make_error(&e, left_id))?; - Type::assert(right_ty, Type::Number).map_err(|e| self.make_error(&e, right_id))?; + Type::assert(left_ty, Type::Number, self.node_map).map_err(|e| self.make_error(&e, left_id))?; + Type::assert(right_ty, Type::Number, self.node_map).map_err(|e| self.make_error(&e, right_id))?; let node = Node::new_arithmetic_op(op, left_id, right_id); let node_id = self.register_node(node); self.symbol_table.set_pos(node_id, self.calc_location(parser_node)?); @@ -631,7 +688,7 @@ impl<'a> HirGenerator<'a> { _ => None, }; if let Some(op) = op { - Type::assert(right_ty, left_ty).map_err(|e| self.make_low_error(&e, parser_node))?; // TODO: improve error message + Type::assert(right_ty, left_ty, self.node_map).map_err(|e| self.make_low_error(&e, parser_node))?; // TODO: improve error message let node = Node::new_relational_op(op, left_ty, left_id, right_id); let node_id = self.register_node(node); self.symbol_table.set_pos(node_id, self.calc_location(parser_node)?); @@ -647,8 +704,8 @@ impl<'a> HirGenerator<'a> { _ => None, }; if let Some(op) = op { - Type::assert(left_ty, Type::Bool).map_err(|e| self.make_error(&e, left_id))?; - Type::assert(right_ty, Type::Bool).map_err(|e| self.make_error(&e, right_id))?; + Type::assert(left_ty, Type::Bool, self.node_map).map_err(|e| self.make_error(&e, left_id))?; + Type::assert(right_ty, Type::Bool, self.node_map).map_err(|e| self.make_error(&e, right_id))?; let node = Node::new_logical_binary_op(op, left_id, right_id); let node_id = self.register_node(node); self.symbol_table.set_pos(node_id, self.calc_location(parser_node)?); @@ -661,7 +718,7 @@ impl<'a> HirGenerator<'a> { ast::Node::CallExpr(call_expr) => { if self.trace { println!("enter expr (node: {})", parser_node.get_name()); } let callee_id = self.generate_expr(&call_expr.callee)?; - let callee_func_id = self.resolve_node(callee_id); + let callee_func_id = self.resolve_identifier(callee_id); let callee_func = callee_func_id.get(self.node_map).as_decl().map_err(|e| self.make_error(&e, callee_id))?; let signature = callee_func.signature.as_function_signature().map_err(|e| self.make_error(&e, callee_id))?; let ret_ty = signature.ret_ty; @@ -683,7 +740,7 @@ impl<'a> HirGenerator<'a> { if arg_ty == Type::Void { return Err(self.make_error("A function call that does not return a value cannot be used as an expression.", arg_id)); } - Type::assert(arg_ty, param_ty).map_err(|e| self.make_error(&e, arg_id))?; + Type::assert(arg_ty, param_ty, self.node_map).map_err(|e| self.make_error(&e, arg_id))?; args.push(arg_id); } let node = Node::new_call_expr(callee_id, args); @@ -692,14 +749,90 @@ impl<'a> HirGenerator<'a> { self.symbol_table.set_ty(node_id, ret_ty); Ok(node_id) } + ast::Node::FieldAccess(expr) => { + if self.trace { println!("enter expr (node: {})", parser_node.get_name()); } + fn resolve_to_struct_expr<'b>(node_id: NodeId, ctx: &'b HirGenerator) -> Result<&'b StructExpr, String> { + match node_id.get(ctx.node_map) { + Node::Identifier(ident) => { + // Identifier -> Identifier dest + resolve_to_struct_expr(ident.dest, ctx) + } + Node::Declaration(decl) => { + // Declaration -> Declaration body + decl.signature.as_variable_signature().map_err(|_| "variable declaration expected")?; + let decl_body_id = match ctx.get_decl_body(node_id) { + Some(x) => x, + None => return Err("variable is not defined".to_owned()), + }; + let variable = decl_body_id.get(ctx.node_map).as_variable()?; + let variable_body_id = variable.content; + resolve_to_struct_expr(variable_body_id, ctx) + } + Node::StructExpr(struct_expr) => { + Ok(struct_expr) + } + _ => Err("resolve failed. unexpected node type".to_owned()), + } + } + let target_id = self.generate_expr(&expr.target)?; + let struct_expr = resolve_to_struct_expr(target_id, self).map_err(|e| self.make_error(&e, target_id))?; + let struct_expr_field_id = match struct_expr.field_table.get(&expr.name) { + Some(&x) => x, + None => return Err(self.make_error("unknown field name", target_id)), + }; + let field_ty = self.get_ty_or_err(struct_expr_field_id)?; + // make node + let node = Node::new_field_access(expr.name.clone(), target_id); + let node_id = self.register_node(node); + self.symbol_table.set_pos(node_id, self.calc_location(parser_node)?); + self.symbol_table.set_ty(node_id, field_ty); + Ok(node_id) + } + ast::Node::StructExpr(struct_expr) => { + if self.trace { println!("enter expr (node: {})", parser_node.get_name()); } + let ty = Type::from_name(&struct_expr.name, &self.resolver, self.node_map).map_err(|e| self.make_low_error(&e, parser_node))?; + let struct_decl_id = match ty { + Type::Struct(x) => x, + _ => return Err(self.make_low_error("struct type expected", parser_node)), + }; + let mut fields = BTreeMap::new(); + for n in struct_expr.fields.iter() { + let struct_decl = struct_decl_id.get(self.node_map).as_decl().map_err(|e| self.make_low_error(&e, parser_node))?; + let struct_signature = struct_decl.signature.as_struct_signature().map_err(|e| self.make_low_error(&e, parser_node))?; + let expr_field_node = n.as_struct_expr_field(); + let decl_field_id = match struct_signature.field_table.get(&expr_field_node.name) { + Some(&x) => x, + None => return Err(self.make_low_error("unknown field name", parser_node)), + }; + let expr_field_body_id = self.generate_expr(&expr_field_node.body)?; + let decl_field_ty = self.get_ty_or_err(decl_field_id)?; + let expr_field_body_ty = self.get_ty_or_err(expr_field_body_id)?; + Type::assert(expr_field_body_ty, decl_field_ty, self.node_map).map_err(|e| self.make_error(&e, expr_field_body_id))?; + // make field node + let node = Node::new_struct_expr_field(expr_field_body_id); + let node_id = self.register_node(node); + self.symbol_table.set_pos(node_id, self.calc_location(n)?); + self.symbol_table.set_ty(node_id, expr_field_body_ty); + fields.insert(expr_field_node.name.clone(), node_id); + } + // make node + let node = Node::new_struct_expr(struct_expr.name.clone(), fields); + let node_id = self.register_node(node); + self.symbol_table.set_pos(node_id, self.calc_location(parser_node)?); + self.symbol_table.set_ty(node_id, ty); + Ok(node_id) + } ast::Node::FunctionDeclaration(_) | ast::Node::VariableDeclaration(_) + | ast::Node::StructDeclaration(_) | ast::Node::BreakStatement(_) | ast::Node::ReturnStatement(_) | ast::Node::Assignment(_) | ast::Node::IfStatement(_) | ast::Node::LoopStatement(_) - | ast::Node::FuncParam(_) => { + | ast::Node::FuncParam(_) + | ast::Node::StructDeclField(_) + | ast::Node::StructExprField(_) => { if self.trace { println!("enter expr (node: {})", parser_node.get_name()); } panic!("unexpected expr node"); } diff --git a/uguisu-engine/src/hir_run.rs b/uguisu-engine/src/hir_run.rs index 79cf21c5..7bbc60b0 100644 --- a/uguisu-engine/src/hir_run.rs +++ b/uguisu-engine/src/hir_run.rs @@ -34,6 +34,7 @@ pub(crate) enum Value { Bool(bool), String(String), Function(NodeId), + Struct((NodeId, BTreeMap)), } impl Value { @@ -42,29 +43,30 @@ impl Value { Value::Number(_) => Type::Number, Value::Bool(_) => Type::Bool, Value::String(_) => Type::String, + Value::Struct((node_id, _)) => Type::Struct(node_id.clone()), Value::Function(_) => panic!("get type error"), Value::NoneValue => panic!("get type error"), } } - pub(crate) fn as_number(&self) -> i64 { + pub(crate) fn as_number(&self, node_map: &BTreeMap) -> i64 { match self { &Value::Number(value) => value, - _ => panic!("type mismatched. expected `number`, found `{}`", self.get_type().get_name()), + _ => panic!("type mismatched. expected `number`, found `{}`", self.get_type().get_name(node_map)), } } - pub(crate) fn as_bool(&self) -> bool { + pub(crate) fn as_bool(&self, node_map: &BTreeMap) -> bool { match self { &Value::Bool(value) => value, - _ => panic!("type mismatched. expected `bool`, found `{}`", self.get_type().get_name()), + _ => panic!("type mismatched. expected `bool`, found `{}`", self.get_type().get_name(node_map)), } } - pub(crate) fn as_string(&self) -> &str { + pub(crate) fn as_string(&self, node_map: &BTreeMap) -> &str { match &self { Value::String(value) => value, - _ => panic!("type mismatched. expected `string`, found `{}`", self.get_type().get_name()), + _ => panic!("type mismatched. expected `string`, found `{}`", self.get_type().get_name(node_map)), } } } @@ -144,9 +146,9 @@ impl<'a> HirRunner<'a> { } } - fn resolve_node(&self, node_id: NodeId) -> NodeId { + fn resolve_identifier(&self, node_id: NodeId) -> NodeId { match node_id.get(self.source) { - Node::Reference(reference) => self.resolve_node(reference.dest), + Node::Identifier(identifier) => self.resolve_identifier(identifier.dest), _ => node_id, } } @@ -191,7 +193,7 @@ impl<'a> HirRunner<'a> { env.set_symbol(node_id, Value::Function(node_id)); } Signature::VariableSignature(_) => { - match self.symbol_table.get(node_id).body { + match self.symbol_table.get(node_id).decl_body { Some(body) => { let value = self.eval_expr(body, env)?; env.set_symbol(node_id, value); @@ -199,6 +201,7 @@ impl<'a> HirRunner<'a> { None => {} // variable is not defined yet } } + Signature::StructSignature(_) => {} } Ok(StatementResult::None) } @@ -218,14 +221,14 @@ impl<'a> HirRunner<'a> { let curr_value = self.eval_expr(statement.dest, env)?; match statement.mode { AssignmentMode::Assign => { - let dest_id = self.resolve_node(statement.dest); + let dest_id = self.resolve_identifier(statement.dest); let value = self.eval_expr(statement.body, env)?; env.set_symbol(dest_id, value); } AssignmentMode::AddAssign => { - let dest_id = self.resolve_node(statement.dest); - let restored_value = curr_value.as_number(); - let body_value = self.eval_expr(statement.body, env)?.as_number(); + let dest_id = self.resolve_identifier(statement.dest); + let restored_value = curr_value.as_number(self.source); + let body_value = self.eval_expr(statement.body, env)?.as_number(self.source); let value = match restored_value.checked_add(body_value) { Some(x) => x, None => return Err(RuntimeError::new("add operation overflowed")), @@ -233,9 +236,9 @@ impl<'a> HirRunner<'a> { env.set_symbol(dest_id, Value::Number(value)); } AssignmentMode::SubAssign => { - let dest_id = self.resolve_node(statement.dest); - let restored_value = curr_value.as_number(); - let body_value = self.eval_expr(statement.body, env)?.as_number(); + let dest_id = self.resolve_identifier(statement.dest); + let restored_value = curr_value.as_number(self.source); + let body_value = self.eval_expr(statement.body, env)?.as_number(self.source); let value = match restored_value.checked_sub(body_value) { Some(x) => x, None => return Err(RuntimeError::new("sub operation overflowed")), @@ -243,9 +246,9 @@ impl<'a> HirRunner<'a> { env.set_symbol(dest_id, Value::Number(value)); } AssignmentMode::MultAssign => { - let dest_id = self.resolve_node(statement.dest); - let restored_value = curr_value.as_number(); - let body_value = self.eval_expr(statement.body, env)?.as_number(); + let dest_id = self.resolve_identifier(statement.dest); + let restored_value = curr_value.as_number(self.source); + let body_value = self.eval_expr(statement.body, env)?.as_number(self.source); let value = match restored_value.checked_mul(body_value) { Some(x) => x, None => return Err(RuntimeError::new("mult operation overflowed")), @@ -253,9 +256,9 @@ impl<'a> HirRunner<'a> { env.set_symbol(dest_id, Value::Number(value)); } AssignmentMode::DivAssign => { - let dest_id = self.resolve_node(statement.dest); - let restored_value = curr_value.as_number(); - let body_value = self.eval_expr(statement.body, env)?.as_number(); + let dest_id = self.resolve_identifier(statement.dest); + let restored_value = curr_value.as_number(self.source); + let body_value = self.eval_expr(statement.body, env)?.as_number(self.source); let value = match restored_value.checked_div(body_value) { Some(x) => x, None => return Err(RuntimeError::new("div operation overflowed")), @@ -263,9 +266,9 @@ impl<'a> HirRunner<'a> { env.set_symbol(dest_id, Value::Number(value)); } AssignmentMode::ModAssign => { - let dest_id = self.resolve_node(statement.dest); - let restored_value = curr_value.as_number(); - let body_value = self.eval_expr(statement.body, env)?.as_number(); + let dest_id = self.resolve_identifier(statement.dest); + let restored_value = curr_value.as_number(self.source); + let body_value = self.eval_expr(statement.body, env)?.as_number(self.source); let value = match restored_value.checked_rem(body_value) { Some(x) => x, None => return Err(RuntimeError::new("mod operation overflowed")), @@ -276,7 +279,7 @@ impl<'a> HirRunner<'a> { Ok(StatementResult::None) } Node::IfStatement(statement) => { - let condition = self.eval_expr(statement.condition, env)?.as_bool(); + let condition = self.eval_expr(statement.condition, env)?.as_bool(self.source); let block = if condition { &statement.then_block } else { @@ -302,19 +305,23 @@ impl<'a> HirRunner<'a> { } Ok(result) } - Node::Reference(_) + Node::Identifier(_) | Node::Literal(_) | Node::RelationalOp(_) | Node::LogicalBinaryOp(_) | Node::ArithmeticOp(_) | Node::LogicalUnaryOp(_) - | Node::CallExpr(_) => { + | Node::CallExpr(_) + | Node::FieldAccess(_) + | Node::StructExpr(_) => { self.eval_expr(node_id, env)?; Ok(StatementResult::None) } Node::Function(_) | Node::Variable(_) - | Node::FuncParam(_) => { + | Node::FuncParam(_) + | Node::StructDeclField(_) + | Node::StructExprField(_) => { panic!("Failed to execute the statement: unsupported node (node_id={})", node_id); } }; @@ -333,8 +340,8 @@ impl<'a> HirRunner<'a> { Node::Variable(variable) => { // variable of initial value Ok(self.eval_expr(variable.content, env)?) } - Node::Reference(reference) => { - let dest_id = self.resolve_node(reference.dest); + Node::Identifier(identifier) => { + let dest_id = self.resolve_identifier(identifier.dest); match env.get_symbol(dest_id) { Some(x) => Ok(x.clone()), None => panic!("symbol not found (node_id={}, dest_id={})", node_id, dest_id), @@ -352,8 +359,8 @@ impl<'a> HirRunner<'a> { let right = self.eval_expr(expr.right, env)?; match expr.relation_type { Type::Number => { - let left = left.as_number(); - let right = right.as_number(); + let left = left.as_number(self.source); + let right = right.as_number(self.source); match expr.operator { RelationalOperator::Equal => Ok(Value::Bool(left == right)), RelationalOperator::NotEqual => Ok(Value::Bool(left != right)), @@ -364,8 +371,8 @@ impl<'a> HirRunner<'a> { } } Type::Bool => { - let left = left.as_bool(); - let right = right.as_bool(); + let left = left.as_bool(self.source); + let right = right.as_bool(self.source); match expr.operator { RelationalOperator::Equal => Ok(Value::Bool(left == right)), RelationalOperator::NotEqual => Ok(Value::Bool(left != right)), @@ -377,22 +384,23 @@ impl<'a> HirRunner<'a> { } Type::Function | Type::String - | Type::Void => { + | Type::Void + | Type::Struct(_) => { panic!("unsupported operation (node_id={})", node_id); } } } Node::LogicalBinaryOp(expr) => { - let left = self.eval_expr(expr.left, env)?.as_bool(); - let right = self.eval_expr(expr.right, env)?.as_bool(); + let left = self.eval_expr(expr.left, env)?.as_bool(self.source); + let right = self.eval_expr(expr.right, env)?.as_bool(self.source); match expr.operator { LogicalBinaryOperator::And => Ok(Value::Bool(left && right)), LogicalBinaryOperator::Or => Ok(Value::Bool(left || right)), } } Node::ArithmeticOp(expr) => { - let left = self.eval_expr(expr.left, env)?.as_number(); - let right = self.eval_expr(expr.right, env)?.as_number(); + let left = self.eval_expr(expr.left, env)?.as_number(self.source); + let right = self.eval_expr(expr.right, env)?.as_number(self.source); match expr.operator { ArithmeticOperator::Add => match left.checked_add(right) { Some(x) => Ok(Value::Number(x)), @@ -417,18 +425,18 @@ impl<'a> HirRunner<'a> { } } Node::LogicalUnaryOp(op) => { - let expr = self.eval_expr(op.expr, env)?.as_bool(); + let expr = self.eval_expr(op.expr, env)?.as_bool(self.source); match op.operator { LogicalUnaryOperator::Not => Ok(Value::Bool(!expr)), } } Node::CallExpr(call_expr) => { - let callee_id = self.resolve_node(call_expr.callee); + let callee_id = self.resolve_identifier(call_expr.callee); let callee = callee_id.get(self.source).as_decl().unwrap(); let signature = callee.signature.as_function_signature().unwrap(); - let func = match self.symbol_table.get(callee_id).body { + let func = match self.symbol_table.get(callee_id).decl_body { Some(x) => x.get(self.source).as_function().unwrap(), - None => panic!("function `{}` is not defined (node_id={})", callee.identifier, call_expr.callee), + None => panic!("function `{}` is not defined (node_id={})", callee.name, call_expr.callee), }; let mut args = Vec::new(); for &arg_id in call_expr.args.iter() { @@ -457,7 +465,7 @@ impl<'a> HirRunner<'a> { } } FunctionBody::NativeCode => { - result = Some(self.builtins.call(&callee.identifier, &args)?); + result = Some(self.builtins.call(&callee.name, &args, self.source)?); } } env.pop_frame(); @@ -467,8 +475,16 @@ impl<'a> HirRunner<'a> { }; Ok(value) } + Node::StructExpr(_expr) => { + todo!(); // TODO: 構造体のインスタンスを返す + } + Node::FieldAccess(_expr) => { + todo!(); // TODO: 構造体のフィールドの値を取得して返す + } Node::Function(_) => panic!("function object unsupported (node_id={})", node_id), Node::FuncParam(_) + | Node::StructDeclField(_) + | Node::StructExprField(_) | Node::Declaration(_) | Node::ReturnStatement(_) | Node::BreakStatement(_) diff --git a/uguisu-engine/src/parse.rs b/uguisu-engine/src/parse.rs index 22313384..3ffeb696 100644 --- a/uguisu-engine/src/parse.rs +++ b/uguisu-engine/src/parse.rs @@ -59,6 +59,7 @@ peg::parser! { pub(crate) rule statement() -> Node = function_declaration() + / struct_declaration() / break_statement() / return_statement() / variable_declaration() @@ -91,17 +92,55 @@ peg::parser! { // p:pos() "+" __* expr:(@) { Node::new_unary_op("+", expr, p) } // p:pos() "-" __* expr:(@) { Node::new_unary_op("-", expr, p) } -- - e:number() { e } - e:bool() { e } - e:string() { e } - e:call_expr() { e } - p:pos() id:idenfitier() { Node::new_reference(id, p) } - "(" __* e:expression() __* ")" { e } + expr:expr_factor() field:(__* x:field_access() { x })? { + /// build the field access chain + /// ```text + /// expr: "x", field: ["aaa", "bbb", "ccc"] + /// | + /// v + /// "ccc" { + /// "bbb" { + /// "aaa" { + /// "x" { } + /// } + /// } + /// } + /// ``` + fn build_node(i: usize, segments: &Vec<(&str, usize)>, expr: Node) -> Node { + if i >= segments.len() { + expr + } else { + match segments.get(segments.len() - i - 1) { + Some(x) => { + Node::new_field_access( + x.0.to_owned(), + build_node(i + 1, segments, expr), + x.1, + ) + } + None => panic!(), + } + } + } + match field { + Some(x) => build_node(0, &x, expr), + None => expr, + } + } } + rule expr_factor() -> Node + = number() + / bool() + / string() + / call_expr() + / struct_expr() + / p:pos() id:idenfitier() { Node::new_identifier(id, p) } + / "(" __* e:expression() __* ")" { e } + rule function_declaration() -> Node = p:pos() attrs:func_dec_attrs()? "fn" __+ name:idenfitier() __* "(" __* params:func_dec_params()? __* ")" - __* ret:func_dec_return_type()? __* body:func_dec_body() + __* ret:type_label()? __* body:func_dec_body() { let params = if let Some(v) = params { v } else { vec![] }; let attrs = if let Some(v) = attrs { v } else { vec![] }; @@ -115,7 +154,7 @@ peg::parser! { = p:pos() name:idenfitier() type_name:(__* ":" __* n:idenfitier() { n.to_string() })? { Node::new_func_param(name.to_string(), type_name, p) } - rule func_dec_return_type() -> String + rule type_label() -> String = ":" __* type_name:idenfitier() { type_name.to_string() } rule func_dec_body() -> Option> @@ -129,6 +168,38 @@ peg::parser! { // rule func_dec_attr() -> FunctionAttribute // = "" { } + rule struct_declaration() -> Node = + p:pos() "struct" __+ name:idenfitier() __* "{" __* body:(struct_decl_field() ++ (__*))? __* "}" + { + let body = match body { + Some(x) => x, + None => vec![], + }; + Node::new_struct_declaration(name.to_string(), body, p) + } + + rule struct_decl_field() -> Node = + p:pos() name:idenfitier() __* type_name:type_label() __* ";" + { + Node::new_struct_decl_field(name.to_string(), type_name, p) + } + + rule struct_expr() -> Node = + p:pos() name:idenfitier() __* "{" __* body:(struct_expr_field() ++ (__* "," __*))? __* ("," __*)? "}" + { + let body = match body { + Some(x) => x, + None => vec![], + }; + Node::new_struct_expr(name.to_string(), body, p) + } + + rule struct_expr_field() -> Node = + p:pos() name:idenfitier() __* ":" __* body:expression() + { + Node::new_struct_expr_field(name.to_string(), body, p) + } + rule break_statement() -> Node = p:pos() "break" __* ";" { Node::new_break_statement(p) } @@ -145,7 +216,7 @@ peg::parser! { rule assignment() -> Node = p:pos() id:idenfitier() __* mode:assignment_mode() __* e:expression() ";" { - Node::new_assignment(Node::new_reference(id, p), e, mode, p) + Node::new_assignment(Node::new_identifier(id, p), e, mode, p) } rule assignment_mode() -> AssignmentMode @@ -180,7 +251,7 @@ peg::parser! { = p:pos() name:idenfitier() __* "(" __* args:call_params()? __* ")" { let args = if let Some(v) = args { v } else { vec![] }; - Node::new_call_expr(Node::new_reference(name, p), args, p) + Node::new_call_expr(Node::new_identifier(name, p), args, p) } rule call_params() -> Vec @@ -221,6 +292,15 @@ peg::parser! { Node::new_loop_statement(body, p) } + rule field_access() -> Vec<(&'input str, usize)> + = field_access_segment() ++ (__*) + + rule field_access_segment() -> (&'input str, usize) + = "." __* p:pos() name:idenfitier() + { + (name, p) + } + rule block() -> Vec = "{" __* s:statements()? __* "}" { if let Some(nodes) = s { diff --git a/uguisu-engine/src/parse/test.rs b/uguisu-engine/src/parse/test.rs index 4e7a997a..cea08186 100644 --- a/uguisu-engine/src/parse/test.rs +++ b/uguisu-engine/src/parse/test.rs @@ -147,20 +147,20 @@ fn test_declare_func_with_types_4() { #[test] fn test_identifier_single_ascii() { - if let Ok(Node::Reference(Reference { identifier, pos: 0, .. })) = uguisu_parser::expression("a") { - assert_eq!(identifier, "a"); + if let Ok(Node::Identifier(Identifier { name, pos: 0, .. })) = uguisu_parser::expression("a") { + assert_eq!(name, "a"); } else { panic!("incorrect result 1"); } - if let Ok(Node::Reference(Reference { identifier, pos: 0, .. })) = uguisu_parser::expression("z") { - assert_eq!(identifier, "z"); + if let Ok(Node::Identifier(Identifier { name, pos: 0, .. })) = uguisu_parser::expression("z") { + assert_eq!(name, "z"); } else { panic!("incorrect result 2"); } - if let Ok(Node::Reference(Reference { identifier, pos: 0, .. })) = uguisu_parser::expression("_") { - assert_eq!(identifier, "_"); + if let Ok(Node::Identifier(Identifier { name, pos: 0, .. })) = uguisu_parser::expression("_") { + assert_eq!(name, "_"); } else { panic!("incorrect result 3"); } @@ -172,8 +172,8 @@ fn test_identifier_single_ascii() { #[test] fn test_identifier_multi_ascii() { - if let Ok(Node::Reference(Reference { identifier, pos: 0, .. })) = uguisu_parser::expression("abc") { - assert_eq!(identifier, "abc"); + if let Ok(Node::Identifier(Identifier { name, pos: 0, .. })) = uguisu_parser::expression("abc") { + assert_eq!(name, "abc"); } else { panic!("incorrect result"); } @@ -185,14 +185,14 @@ fn test_identifier_multi_ascii() { #[test] fn test_identifier_multi_byte() { - if let Ok(Node::Reference(Reference { identifier, pos: 0, .. })) = uguisu_parser::expression("あ") { - assert_eq!(identifier, "あ"); + if let Ok(Node::Identifier(Identifier { name, pos: 0, .. })) = uguisu_parser::expression("あ") { + assert_eq!(name, "あ"); } else { panic!("incorrect result"); } - if let Ok(Node::Reference(Reference { identifier, pos: 0, .. })) = uguisu_parser::expression("変数1") { - assert_eq!(identifier, "変数1"); + if let Ok(Node::Identifier(Identifier { name, pos: 0, .. })) = uguisu_parser::expression("変数1") { + assert_eq!(name, "変数1"); } else { panic!("incorrect result"); } diff --git a/uguisu-engine/src/test.rs b/uguisu-engine/src/test.rs index 44d80654..66d713fc 100644 --- a/uguisu-engine/src/test.rs +++ b/uguisu-engine/src/test.rs @@ -461,7 +461,7 @@ fn test_assignment_modes() { ); } -// function reference +// function identifier #[test] fn should_generate_error_with_function_name_1() { @@ -638,6 +638,91 @@ fn test_string_literal() { ); } +// struct declaration + +#[test] +fn test_struct_decl() { + run_test( + " + struct Data { + value: number; + } + fn main() { } + ", + ); +} + +// struct instance + +#[test] +fn test_struct_instance() { + run_test( + " + struct Data { + value: number; + } + fn main() { + var x: Data = Data { + value: 1, + }; + } + ", + ); +} + +// struct field access + +#[test] +fn test_struct_field_access_1() { + run_test( + " + struct Data { + value: number; + } + fn main() { + var x: Data = Data { + value: 1, + }; + var y: number = x.value; + } + ", + ); +} + +#[test] +fn test_struct_field_access_2() { + run_test( + " + struct Data { + value: number; + } + fn main() { + var x: Data = Data { + value: 1, + }; + var y: Data = x; + var z: number = y.value; + } + ", + ); +} + +#[test] +fn test_struct_field_access_3() { + run_test( + " + struct Data { + value: number; + } + fn main() { + var x: number = Data { + value: 1, + }.value; + } + ", + ); +} + // other examples #[test]