diff --git a/include/Ast/InitExpr.hpp b/include/Ast/InitExpr.hpp index f5cbf01..0f76c0a 100644 --- a/include/Ast/InitExpr.hpp +++ b/include/Ast/InitExpr.hpp @@ -11,13 +11,27 @@ namespace Fig::Ast std::vector> args; + enum class InitMode + { + Positional = 1, + Named, + Shorthand + } initMode; + + /* + 3 ways of calling constructor + .1 Person {"Fig", 1, "IDK"}; + .2 Person {name: "Fig", age: 1, sex: "IDK"}; // can be unordered + .3 Person {name, age, sex}; + */ + InitExprAst() { type = AstType::InitExpr; } - InitExprAst(FString _structName, std::vector> _args) : - structName(std::move(_structName)), args(std::move(_args)) + InitExprAst(FString _structName, std::vector> _args, InitMode _initMode) : + structName(std::move(_structName)), args(std::move(_args)), initMode(_initMode) { type = AstType::InitExpr; } diff --git a/include/Ast/VarAssignSt.hpp b/include/Ast/VarAssignSt.hpp deleted file mode 100644 index 861c5d8..0000000 --- a/include/Ast/VarAssignSt.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include - -namespace Fig::Ast -{ - class VarAssignSt final : public StatementAst - { - public: - const FString varName; - const Expression valueExpr; - - VarAssignSt() - { - type = AstType::VarAssignSt; - } - - VarAssignSt(FString _varName, Expression _valueExpr) : - varName(std::move(_varName)), valueExpr(std::move(_valueExpr)) - { - type = AstType::VarAssignSt; - } - }; - - using VarAssign = std::shared_ptr; -}; // namespace Fig \ No newline at end of file diff --git a/include/Ast/astBase.hpp b/include/Ast/astBase.hpp index d7ba424..370be62 100644 --- a/include/Ast/astBase.hpp +++ b/include/Ast/astBase.hpp @@ -25,7 +25,6 @@ namespace Fig::Ast UnaryExpr, BinaryExpr, TernaryExpr, - ListExpr, // [] TupleExpr, // () MapExpr, // {} @@ -215,7 +214,8 @@ namespace Fig::Ast ShiftRight, // >> // 赋值表达式 - Walrus, // := + Assign, // = + // Walrus, // := // 点运算符 . Dot, @@ -250,7 +250,7 @@ namespace Fig::Ast Operator::ShiftLeft, Operator::ShiftRight, - Operator::Walrus, + // Operator::Walrus, Operator::Dot}; static const std::unordered_set ternaryOps{Operator::TernaryCond}; @@ -290,7 +290,7 @@ namespace Fig::Ast {TokenType::ShiftRight, Operator::ShiftRight}, // 赋值表达式 - {TokenType::Walrus, Operator::Walrus}, + // {TokenType::Walrus, Operator::Walrus}, // 点运算符 {TokenType::Dot, Operator::Dot}, }; // := diff --git a/include/Value/Type.hpp b/include/Value/Type.hpp index 8b83c66..78e02b2 100644 --- a/include/Value/Type.hpp +++ b/include/Value/Type.hpp @@ -4,8 +4,6 @@ #include #include -#include -#include namespace Fig { diff --git a/include/Value/structInstance.hpp b/include/Value/structInstance.hpp index 4b5f0ac..cdcc432 100644 --- a/include/Value/structInstance.hpp +++ b/include/Value/structInstance.hpp @@ -8,14 +8,14 @@ namespace Fig { struct StructInstanceT final { - FString structName; // 类的名字 (StructType), 非变量名。用于获取所属类 + size_t parentId; ContextPtr localContext; - StructInstanceT(FString _structName, ContextPtr _localContext) : - structName(std::move(_structName)), localContext(std::move(_localContext)) {} + StructInstanceT(size_t _parentId, ContextPtr _localContext) : + parentId(std::move(_parentId)), localContext(std::move(_localContext)) {} StructInstanceT(const StructInstanceT &other) : - structName(other.structName), localContext(other.localContext) {} + parentId(other.parentId), localContext(other.localContext) {} StructInstanceT &operator=(const StructInstanceT &) = default; StructInstanceT(StructInstanceT &&) = default; StructInstanceT &operator=(StructInstanceT &&) = default; @@ -31,10 +31,10 @@ namespace Fig { data = std::make_unique(x); } - StructInstance(FString _structName, ContextPtr _localContext) : + StructInstance(size_t _parentId, ContextPtr _localContext) : __ValueWrapper(ValueType::StructInstance) { - data = std::make_unique(std::move(_structName), std::move(_localContext)); + data = std::make_unique(std::move(_parentId), std::move(_localContext)); } bool operator==(const StructInstance &other) const noexcept diff --git a/include/ast.hpp b/include/ast.hpp index 93d29a2..6eede29 100644 --- a/include/ast.hpp +++ b/include/ast.hpp @@ -1,6 +1,7 @@ #pragma once #include + #include #include #include @@ -18,7 +19,6 @@ #include #include #include -#include #include #include #include \ No newline at end of file diff --git a/include/context.hpp b/include/context.hpp index 3fbb2dc..c9db521 100644 --- a/include/context.hpp +++ b/include/context.hpp @@ -20,6 +20,8 @@ namespace Fig std::unordered_map functions; std::unordered_map functionNames; + + std::unordered_map structTypeNames; public: ContextPtr parent; @@ -129,6 +131,11 @@ namespace Fig functions[fn.id] = fn; functionNames[fn.id] = name; } + if (ti == ValueType::StructType) + { + auto &st = value.as().getValue(); + structTypeNames[st.id] = name; + } } std::optional getFunction(std::size_t id) { @@ -162,6 +169,22 @@ namespace Fig return std::nullopt; } } + std::optional getStructName(std::size_t id) + { + auto it = structTypeNames.find(id); + if (it != structTypeNames.end()) + { + return it->second; + } + else if (parent) + { + return parent->getFunctionName(id); + } + else + { + return std::nullopt; + } + } bool contains(const FString &name) { if (variables.contains(name)) diff --git a/include/parser.hpp b/include/parser.hpp index a104086..c52f519 100644 --- a/include/parser.hpp +++ b/include/parser.hpp @@ -302,7 +302,6 @@ namespace Fig Ast::ValueExpr __parseValueExpr(); Ast::FunctionParameters __parseFunctionParameters(); // entry: current is Token::LeftParen Ast::BlockStatement __parseBlockStatement(); // entry: current is Token::LeftBrace - Ast::VarAssign __parseVarAssign(FString); // entry: current is Token::Assign, para1 is var name Ast::If __parseIf(); // entry: current is Token::If Ast::While __parseWhile(); // entry: current is Token::While Ast::Statement __parseIncrementStatement(); // only allowed in __parseFor function diff --git a/include/token.hpp b/include/token.hpp index b84ae24..fae6465 100644 --- a/include/token.hpp +++ b/include/token.hpp @@ -126,12 +126,16 @@ namespace Fig line = _line; column = _column; } - Token setPos(size_t _line, size_t _column) + const Token& setPos(size_t _line, size_t _column) { line = _line; column = _column; return *this; } + size_t getLength() + { + return value.length(); + } const FString& getValue() const { return value; diff --git a/include/value.hpp b/include/value.hpp index 258ffd6..52adc12 100644 --- a/include/value.hpp +++ b/include/value.hpp @@ -182,7 +182,7 @@ namespace Fig if (is()) { return FString(std::format("().getValue().structName.toBasicString(), + as().getValue().parentId, static_cast(as().data.get()))); } return FString(u8""); diff --git a/src/evaluator.cpp b/src/evaluator.cpp index bc18913..2b2f818 100644 --- a/src/evaluator.cpp +++ b/src/evaluator.cpp @@ -34,10 +34,10 @@ namespace Fig case Operator::ShiftLeft: return shift_left(lhs, rhs); case Operator::ShiftRight: return shift_right(lhs, rhs); - case Operator::Walrus: { - static constexpr char WalrusErrorName[] = "WalrusError"; - throw EvaluatorError(FStringView(u8"Walrus operator is not supported"), currentAddressInfo); // using parent address info for now - } + // case Operator::Walrus: { + // static constexpr char WalrusErrorName[] = "WalrusError"; + // throw EvaluatorError(FStringView(u8"Walrus operator is not supported"), currentAddressInfo); // using parent address info for now + // } default: throw RuntimeError(FStringView(u8"Unsupported operator")); } @@ -45,6 +45,55 @@ namespace Fig Value Evaluator::evalBinary(const Ast::BinaryExpr &binExp) { + if (binExp->op == Ast::Operator::Dot) + { + const Value &lhs = eval(binExp->lexp); + if (!lhs.is()) + { + static constexpr char AccessOpObjectNotStructError[] = "AccessOpObjectNotStructError"; + throw EvaluatorError(FStringView( + std::format("Object not a struct")), + binExp->lexp->getAAI()); + } + const StructInstanceT &st = lhs.as().getValue(); + Ast::VarExpr varExp; + if (!(varExp = std::dynamic_pointer_cast(binExp->rexp))) + { + static constexpr char AccessOpNotAFieldNameError[] = "AccessOpNotAFieldNameError"; + throw EvaluatorError(FStringView( + std::format("{} is not a field name", binExp->rexp->toString().toBasicString())), + binExp->rexp->getAAI()); + } + FString member = varExp->name; + auto structTypeNameOpt = currentContext->getStructName(st.parentId); + if (!structTypeNameOpt) throw RuntimeError(FStringView("Can't get struct type name")); + FString structTypeName = *structTypeNameOpt; + if (!st.localContext->containsInThisScope(member)) + { + static constexpr char NoAttributeError[] = "NoAttributeError"; + throw EvaluatorError(FStringView( + std::format("Struct `{}` has no attribute '{}'", structTypeName.toBasicString(), member.toBasicString())), + binExp->rexp->getAAI()); + } + return *st.localContext->get(member); // safe + } + if (binExp->op == Ast::Operator::Assign) + { + Ast::VarExpr varExp; + if (!(varExp = std::dynamic_pointer_cast(binExp->rexp))) + { + static constexpr char AssignToRightValueError[] = "AssignToRightValueError"; + throw EvaluatorError(FStringView( + std::format("Can't assign to right value {}", binExp->lexp->toString().toBasicString())), + binExp->lexp->getAAI()); + } + const FString& varName = varExp->name; + if (!currentContext->contains(varName)) + { + static constexpr char VariableNotFoundErrorName[] = "VariableNotFoundError"; + throw EvaluatorError(FStringView(std::format("Variable '{}' not defined", varName.toBasicString())), currentAddressInfo); + } + } return __evalOp(binExp->op, eval(binExp->lexp), eval(binExp->rexp)); } Value Evaluator::evalUnary(const Ast::UnaryExpr &unExp) @@ -253,8 +302,133 @@ namespace Fig currentContext); } } - case AstType::ListExpr: { - auto listexpr = std::dynamic_pointer_cast(exp); + case AstType::InitExpr: { + auto initExpr = std::dynamic_pointer_cast(exp); + if (!currentContext->contains(initExpr->structName)) + { + static constexpr char StructNotFoundErrorName[] = "StructNotFoundError"; + throw EvaluatorError(FStringView(std::format("Structure type '{}' not found", initExpr->structName.toBasicString())), initExpr->getAAI()); + } + Value structTypeVal = currentContext->get(initExpr->structName).value(); + if (!structTypeVal.is()) + { + static constexpr char NotAStructTypeErrorName[] = "NotAStructTypeError"; + throw EvaluatorError(FStringView(std::format("'{}' is not a structure type", initExpr->structName.toBasicString())), initExpr->getAAI()); + } + const StructT &structT = structTypeVal.as().getValue(); + ContextPtr defContext = structT.defContext; // definition context + // check init args + + size_t minArgs = 0; + size_t maxArgs = structT.fields.size(); + + for (auto &f : structT.fields) + { + if (f.defaultValue == nullptr) minArgs++; + } + + size_t got = initExpr->args.size(); + if (got > maxArgs || got < minArgs) + { + static constexpr char StructInitArgumentMismatchErrorName[] = "StructInitArgumentMismatchError"; + throw EvaluatorError(FStringView(std::format("Structure '{}' expects {} to {} fields, but {} were provided", initExpr->structName.toBasicString(), minArgs, maxArgs, initExpr->args.size())), initExpr->getAAI()); + } + + std::vector> evaluatedArgs; + for (const auto &[argName, argExpr] : initExpr->args) + { + evaluatedArgs.push_back({argName, eval(argExpr)}); + } + ContextPtr instanceCtx = std::make_shared( + FString(std::format("", initExpr->structName.toBasicString())), + currentContext); + /* + 3 ways of calling constructor + .1 Person {"Fig", 1, "IDK"}; + .2 Person {name: "Fig", age: 1, sex: "IDK"}; // can be unordered + .3 Person {name, age, sex}; + */ + { + using enum Ast::InitExprAst::InitMode; + if (initExpr->initMode == Positional) + { + for (size_t i = 0; i < maxArgs; ++i) + { + const Field &field = structT.fields[i]; + const FString &fieldName = field.name; + const TypeInfo &expectedType = field.type; + if (i >= evaluatedArgs.size()) + { + // we've checked argument count before, so here must be a default value + ContextPtr previousContext = currentContext; + currentContext = defContext; // evaluate default value in definition context + + Value defaultVal = eval(field.defaultValue); // it can't be null here + + currentContext = previousContext; + + // type check + if (expectedType != defaultVal.getTypeInfo() && expectedType != ValueType::Any) + { + static constexpr char StructFieldTypeMismatchErrorName[] = "StructFieldTypeMismatchError"; + throw EvaluatorError(FStringView(std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", initExpr->structName.toBasicString(), fieldName.toBasicString(), expectedType.toString().toBasicString(), defaultVal.getTypeInfo().toString().toBasicString())), initExpr->getAAI()); + } + + instanceCtx->def(fieldName, expectedType, field.am, defaultVal); + continue; + } + + const Value &argVal = evaluatedArgs[i].second; + if (expectedType != argVal.getTypeInfo() && expectedType != ValueType::Any) + { + static constexpr char StructFieldTypeMismatchErrorName[] = "StructFieldTypeMismatchError"; + throw EvaluatorError(FStringView(std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", initExpr->structName.toBasicString(), fieldName.toBasicString(), expectedType.toString().toBasicString(), argVal.getTypeInfo().toString().toBasicString())), initExpr->getAAI()); + } + instanceCtx->def(fieldName, expectedType, field.am, argVal); + } + } + else + { + // named / shorthand init + for (size_t i = 0; i < maxArgs; ++i) + { + const Field &field = structT.fields[i]; + const FString &fieldName = (field.name.empty() ? evaluatedArgs[i].first : field.name); + if (instanceCtx->containsInThisScope(fieldName)) + { + static constexpr char StructFieldRedeclarationErrorName[] = "StructFieldRedeclarationError"; + throw EvaluatorError(FStringView(std::format("Field '{}' already initialized in structure '{}'", fieldName.toBasicString(), initExpr->structName.toBasicString())), initExpr->getAAI()); + } + if (i + 1 > got) + { + // use default value + ContextPtr previousContext = currentContext; + currentContext = defContext; // evaluate default value in definition context + Value defaultVal = eval(field.defaultValue); // it can't be null here + currentContext = previousContext; + + // type check + const TypeInfo &expectedType = field.type; + if (expectedType != defaultVal.getTypeInfo() && expectedType != ValueType::Any) + { + static constexpr char StructFieldTypeMismatchErrorName[] = "StructFieldTypeMismatchError"; + throw EvaluatorError(FStringView(std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", initExpr->structName.toBasicString(), fieldName.toBasicString(), expectedType.toString().toBasicString(), defaultVal.getTypeInfo().toString().toBasicString())), initExpr->getAAI()); + } + + instanceCtx->def(fieldName, field.type, field.am, defaultVal); + continue; + } + const Value &argVal = evaluatedArgs[i].second; + if (field.type != argVal.getTypeInfo() && field.type != ValueType::Any) + { + static constexpr char StructFieldTypeMismatchErrorName[] = "StructFieldTypeMismatchError"; + throw EvaluatorError(FStringView(std::format("In structure '{}', field '{}' expects type '{}', but got type '{}'", initExpr->structName.toBasicString(), fieldName.toBasicString(), field.type.toString().toBasicString(), argVal.getTypeInfo().toString().toBasicString())), initExpr->getAAI()); + } + instanceCtx->def(fieldName, field.type, field.am, argVal); + } + } + } + return StructInstance(structT.id, instanceCtx); } default: throw RuntimeError(FStringView("Unknown expression type:" + std::to_string(static_cast(exp->getType())))); @@ -359,7 +533,7 @@ namespace Fig }; case AstType::StructSt: { auto stDef = std::dynamic_pointer_cast(stmt); - if (currentContext->contains(stDef->name)) + if (currentContext->containsInThisScope(stDef->name)) { static constexpr char RedeclarationErrorName[] = "RedeclarationError"; throw EvaluatorError(FStringView(std::format("Structure '{}' already defined in this scope", stDef->name.toBasicString())), currentAddressInfo); @@ -377,6 +551,7 @@ namespace Fig } ContextPtr defContext(currentContext); AccessModifier am = (stDef->isPublic ? AccessModifier::PublicConst : AccessModifier::Const); + TypeInfo _(stDef->name, true); // register type name currentContext->def( stDef->name, ValueType::StructType, @@ -386,32 +561,32 @@ namespace Fig fields))); return StatementResult::normal(); } - case AstType::VarAssignSt: { - auto varAssign = std::dynamic_pointer_cast(stmt); - if (!currentContext->contains(varAssign->varName)) - { - static constexpr char VariableNotFoundErrorName[] = "VariableNotFoundError"; - throw EvaluatorError(FStringView(std::format("Variable '{}' not defined", varAssign->varName.toBasicString())), currentAddressInfo); - } - if (!currentContext->isVariableMutable(varAssign->varName)) - { - static constexpr char ConstAssignmentErrorName[] = "ConstAssignmentError"; - throw EvaluatorError(FStringView(std::format("Cannot assign to constant variable '{}'", varAssign->varName.toBasicString())), currentAddressInfo); - } - Value val = eval(varAssign->valueExpr); - if (currentContext->getTypeInfo(varAssign->varName) != ValueType::Any) - { - TypeInfo expectedType = currentContext->getTypeInfo(varAssign->varName); - TypeInfo actualType = val.getTypeInfo(); - if (expectedType != actualType) - { - static constexpr char VariableTypeMismatchErrorName[] = "VariableTypeMismatchError"; - throw EvaluatorError(FStringView(std::format("assigning: Variable '{}' expects type '{}', but got type '{}'", varAssign->varName.toBasicString(), expectedType.toString().toBasicString(), actualType.toString().toBasicString())), currentAddressInfo); - } - } - currentContext->set(varAssign->varName, val); - return StatementResult::normal(); - }; + // case AstType::VarAssignSt: { + // auto varAssign = std::dynamic_pointer_cast(stmt); + // if (!currentContext->contains(varAssign->varName)) + // { + // static constexpr char VariableNotFoundErrorName[] = "VariableNotFoundError"; + // throw EvaluatorError(FStringView(std::format("Variable '{}' not defined", varAssign->varName.toBasicString())), currentAddressInfo); + // } + // if (!currentContext->isVariableMutable(varAssign->varName)) + // { + // static constexpr char ConstAssignmentErrorName[] = "ConstAssignmentError"; + // throw EvaluatorError(FStringView(std::format("Cannot assign to constant variable '{}'", varAssign->varName.toBasicString())), currentAddressInfo); + // } + // Value val = eval(varAssign->valueExpr); + // if (currentContext->getTypeInfo(varAssign->varName) != ValueType::Any) + // { + // TypeInfo expectedType = currentContext->getTypeInfo(varAssign->varName); + // TypeInfo actualType = val.getTypeInfo(); + // if (expectedType != actualType) + // { + // static constexpr char VariableTypeMismatchErrorName[] = "VariableTypeMismatchError"; + // throw EvaluatorError(FStringView(std::format("assigning: Variable '{}' expects type '{}', but got type '{}'", varAssign->varName.toBasicString(), expectedType.toString().toBasicString(), actualType.toString().toBasicString())), currentAddressInfo); + // } + // } + // currentContext->set(varAssign->varName, val); + // return StatementResult::normal(); + // }; case AstType::IfSt: { auto ifSt = std::dynamic_pointer_cast(stmt); Value condVal = eval(ifSt->condition); @@ -522,7 +697,7 @@ namespace Fig // continue to next iteration continue; } - currentContext = loopContext; // let increment statement be in loop context + currentContext = loopContext; // let increment statement be in loop context evalStatement(forSt->incrementSt); // ignore increment statement result } currentContext = previousContext; // restore context diff --git a/src/parser.cpp b/src/parser.cpp index 9a0da8b..742dd1a 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -34,8 +34,10 @@ namespace Fig {Ast::Operator::ShiftLeft, {15, 16}}, {Ast::Operator::ShiftRight, {15, 16}}, + {Ast::Operator::Assign, {2, 1}}, // 右结合 + // 海象运算符 - {Ast::Operator::Walrus, {2, 1}}, // 右结合 + // {Ast::Operator::Walrus, {2, 1}}, // 右结合 // 点运算符 {Ast::Operator::Dot, {40, 41}}, @@ -391,12 +393,6 @@ namespace Fig next(); stmt = __parseStructDef(false); } - else if (isThis(TokenType::Identifier) and isNext(TokenType::Assign)) - { - FString varName = currentToken().getValue(); - next(); // consume identifier - stmt = __parseVarAssign(varName); - } else if (isThis(TokenType::If)) { stmt = __parseIf(); @@ -454,15 +450,6 @@ namespace Fig stmts.push_back(__parseStatement()); } } - Ast::VarAssign Parser::__parseVarAssign(FString varName) - { - // entry: current is `=` - next(); // consume `=` - Ast::Expression exp = parseExpression(0); - expectSemicolon(); - return makeAst(varName, exp); - } - Ast::If Parser::__parseIf() { // entry: current is `if` @@ -555,8 +542,8 @@ namespace Fig incrementStmt = __parseIncrementStatement(); } // after parse increment, semicolon check state restored if (paren) - expectConsume(TokenType::RightParen); // consume `)` if has `(` - expect(TokenType::LeftBrace); // { + expectConsume(TokenType::RightParen); // consume `)` if has `(` + expect(TokenType::LeftBrace); // { Ast::BlockStatement body = __parseBlockStatement(); // auto consume `}` return makeAst(initStmt, condition, incrementStmt, body); } @@ -678,7 +665,7 @@ namespace Fig .2 Person {name: "Fig", age: 1, sex: "IDK"}; // can be unordered .3 Person {name, age, sex}; */ - uint8_t mode = 0; // 0=undetermined, 1=positional, 2=named, 3=shorthand + uint8_t mode; // 0=undetermined, 1=positional, 2=named, 3=shorthand while (!isThis(TokenType::RightBrace)) { @@ -701,7 +688,7 @@ namespace Fig if (mode == 1) { // 1 Person {"Fig", 1, "IDK"}; - Ast::Expression expr = parseExpression(0); + Ast::Expression expr = parseExpression(0, TokenType::Comma, TokenType::RightBrace); args.push_back({FString(), std::move(expr)}); } else if (mode == 2) @@ -712,7 +699,7 @@ namespace Fig next(); // consume identifier expect(TokenType::Colon); next(); // consume colon - Ast::Expression expr = parseExpression(0); + Ast::Expression expr = parseExpression(0, TokenType::Comma, TokenType::RightBrace); args.push_back({fieldName, std::move(expr)}); } else if (mode == 3) @@ -731,12 +718,17 @@ namespace Fig } else if (!isThis(TokenType::RightBrace)) { - throwAddressableError(u8"Expected comma or right brace"); + throwAddressableError(FStringView( + std::format("Expect `,` or `}}` in struct initialization expression, got {}", + currentToken().toString().toBasicString()) + )); } } expect(TokenType::RightBrace); next(); // consume `}` - return makeAst(structName, args); + return makeAst(structName, args, + (mode == 1 ? Ast::InitExprAst::InitMode::Positional : + (mode == 2 ? Ast::InitExprAst::InitMode::Named : Ast::InitExprAst::InitMode::Shorthand))); } Ast::Expression Parser::__parseTupleOrParenExpr() { diff --git a/test.fig b/test.fig index 33bbd94..ce93635 100644 --- a/test.fig +++ b/test.fig @@ -1,9 +1,17 @@ -var call := 0; -func t() +struct Person { - call = call + 1; - __fstdout_println(call); - t(); + name: String; + age: Int = 10; + + public func getName() + { + return name; + } } -t(); \ No newline at end of file +var person = Person{"123"}; + +const print := __fstdout_print; +print(person.name); +person.name = "sb"; +print(person.name); \ No newline at end of file