diff --git a/README.md b/README.md index 665efaf..ce16128 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,40 @@ this is my string 17 --- +### References + +One's counterpart to C's pointers are references, which behave the exact same way. +The main difference is in how you reference/dereference a variable. We all know C's +referencing and dereferencing operators cause some confusion when first learning the language, +and to prevent this, One has designated `ref` and `deref` keywords that are meant to be used like +functions. Here's an example: + +``` +let x int = 6; +let ref_to_x ref(int) = ref(x); +let y int = deref(ref_to_x) # same as y = x +``` + +References to objects are again quite similar to C, with the only real difference being +the access to an object's members. in One, you an access members of an object or of a +reference to an object with the `.` notation. But you cannot access the members if the +reference nesting goes further. For example: + +``` +let u User = User{17} #user has age 17 +print(u.age) # outputs 17 + +let ref_to_u ref(User) = ref(u) +print(ref_to_u.age) # outputs 17 + +let nested_ref ref(ref(User)) = ref(ref_to_u) +print(nested_ref.age) # not valid +``` + +Right now, in error messages, it's likely you will still find references marked as `*`s, such as `int*` + +--- + ### Functions Functions in One are very similar to what you see in languages such as go. @@ -181,6 +215,22 @@ fn change_values(u User) { --- +### Methods + +Defining a method is very similar to declaring a function. You just have to add the name +of the class between parentheses before the function name. + +``` +fn (User) age() void { + this.age++ +} +``` + +as you can see, a variable called `this` is automatically declared in methods and is always +of type `ref` of the class, in this case `ref(User)` + +--- + ### Print Printing is still a work in progress feature, but right now you can print any primitive and non-primitive type. diff --git a/generator.v b/generator.v index 0c9fc67..d7d66f2 100644 --- a/generator.v +++ b/generator.v @@ -14,18 +14,24 @@ fn mangle_var(name string) string { return 'one_var_${name}' } -fn mangle_func(name string) string { +fn mangle_func(name string, class_name ?string) string { if name == 'main' {return 'main'} - return 'one_func_${name}' + mut mangled_name := 'one_func_${name}' + if class_name != none { + mangled_name += '_${class_name}' + } + return mangled_name } fn mangle_struct(name string) string { - return 'one_class_${name}_' + return 'one_class_${name}' } fn get_type_format(type string) string { - dump(type) + if type.contains('*') { + return 'p' + } return match type { 'int', 'bool' {'d'} 'real' {'f'} @@ -45,17 +51,21 @@ fn (mut g Generator) get_print_label(expr Expr) string { } fn (mut g Generator) get_c_type(typ string) string { - return match typ { + astkcount := typ.count('*') + clean := typ.replace('*', '') + return match clean { 'real' {'float'} 'int' {'int32_t'} 'string' {'OneString'} - else {typ} - } + else {clean} + } + '*'.repeat(astkcount) } fn (mut g Generator) mangle_if_class(name string) string { - if g.symbols.lookup_class(name) != none { - return 'struct ${mangle_struct(name)}' + astkcount := name.count('*') + noptr := name.replace('*', '') + if g.symbols.lookup_class(noptr) != none { + return 'struct ${mangle_struct(noptr)}' + '*'.repeat(astkcount) } return name } @@ -119,7 +129,7 @@ fn (mut g Generator) gen_stmt(stmt Stmt) { } FuncDecl { c_type := g.mangle_if_class(g.get_c_type(stmt.ret_type)) - g.out.write_string('${c_type} ${mangle_func(stmt.name)}(') + g.out.write_string('${c_type} ${mangle_func(stmt.name, stmt.class_name)}(') for param in stmt.params { g.gen_stmt(param) if param != stmt.params[stmt.params.len-1]{ @@ -170,6 +180,16 @@ fn (mut g Generator) gen_stmt(stmt Stmt) { fn (mut g Generator) gen_expr(expr Expr) { match expr { + RefExpr { + g.out.write_string('(&') + g.gen_expr(expr.expr) + g.out.write_string(')') + } + DerefExpr { + g.out.write_string('(*') + g.gen_expr(expr.expr) + g.out.write_string(')') + } RealLiteral { g.out.write_string(expr.val.str()) } @@ -243,7 +263,22 @@ fn (mut g Generator) gen_expr(expr Expr) { g.out.write_string(')') } FnCall { - g.out.write_string('${mangle_func(expr.name)}(') + g.out.write_string('${mangle_func(expr.name, none)}(') + for arg in expr.args { + g.gen_expr(arg) + if arg != expr.args[expr.args.len-1]{ + g.out.write_string(', ') + } + } + g.out.write_string(')') + } + MethodCall { + g.out.write_string('${mangle_func(expr.method, expr.from_type)}(') + g.out.write_string('&') + g.gen_expr(expr.from) + if expr.args.len > 0 { + g.out.write_string(', ') + } for arg in expr.args { g.gen_expr(arg) if arg != expr.args[expr.args.len-1]{ @@ -268,7 +303,12 @@ fn (mut g Generator) gen_expr(expr Expr) { } MemberAccess { g.gen_expr(expr.from) - g.out.write_string('.${expr.member}') + match expr.from_type.count('*') { + 0 {g.out.write_string('.')} + 1 {g.out.write_string('->')} + else {panic("Tried accessing member from an object of type ${expr.from_type}")} + } + g.out.write_string('${expr.member}') } else {panic("Unimplemented expression")} } diff --git a/lexer.v b/lexer.v index d7710c0..f6d45c1 100644 --- a/lexer.v +++ b/lexer.v @@ -16,6 +16,8 @@ enum TokenType as u8 { kw_return kw_print kw_class + kw_ref + kw_deref integer real boolean @@ -126,6 +128,8 @@ fn toktype_from_kw(kw string) TokenType { 'print' {.kw_print} 'class' {.kw_class} 'immutable' {.kw_immutable} + 'ref' {.kw_ref} + 'deref' {.kw_deref} else {.unknown} } } @@ -145,7 +149,7 @@ fn is_real(str string) bool { fn is_keyword(str string) bool { return [ - "void", "int", "real", "bool", "string", "if", "else", "elif", "while", "break", "fn", "return", "let", "const", "true", "false", "print", "class", "immutable" + "void", "int", "real", "bool", "string", "if", "else", "elif", "while", "break", "fn", "return", "let", "const", "true", "false", "print", "class", "immutable", "ref", "deref" ].contains(str) } diff --git a/one b/one deleted file mode 100755 index 6ef3e5e..0000000 Binary files a/one and /dev/null differ diff --git a/parser.v b/parser.v index b7b4670..762f7f3 100644 --- a/parser.v +++ b/parser.v @@ -45,6 +45,8 @@ struct FuncSymbolInfo { struct ClassSymbolInfo { name string members_info map[string]VarSymbolInfo +mut: + methods_info map[string]FuncSymbolInfo } struct SymbolTable { @@ -112,6 +114,19 @@ fn (mut s SymbolTable) lookup_class(name string) ?ClassSymbolInfo { return none } +fn (mut s SymbolTable) define_method(name string, class_name string, typ string, block Block) { + if s.lookup_class(class_name) == none {parse_error("Undefined class ${class_name}")} + s.structs[class_name].methods_info[name] = FuncSymbolInfo{type: typ, block: block} +} + +fn (mut s SymbolTable) lookup_method(name string, class_name string) ?FuncSymbolInfo { + mut classinfo := s.lookup_class(class_name) or {parse_error("Undefined class ${class_name}")} + if name in classinfo.methods_info { + return classinfo.methods_info[name] + } + return none +} + fn (mut s SymbolTable) is_in_global_scope() bool { return s.variable_scopes.len == 1 } @@ -121,10 +136,18 @@ fn (mut s SymbolTable) is_in_global_scope() bool { type Expr = VoidExpr | UnaryExpr | BinaryExpr | IntegerLiteral | RealLiteral | BoolLiteral | StringLiteral | Variable | TypeExpr | Function | TypeCast | ParenExpr | PrintExpr | FnCall | ClassMember | - ClassInstantiation | MemberAccess + ClassInstantiation | MemberAccess | MethodCall | RefExpr | DerefExpr struct VoidExpr {} +struct RefExpr { + expr Expr +} + +struct DerefExpr { + expr Expr +} + struct UnaryExpr { expr Expr op string @@ -169,6 +192,7 @@ struct ClassMember { struct MemberAccess { from Expr + from_type string member string member_type string } @@ -201,6 +225,14 @@ struct FnCall { args []Expr } +struct MethodCall { + from Expr + from_type string + method string + ret_type string + args []Expr +} + // ------------------------------------------- Statements type Stmt = VarDecl | ExprStmt | ReturnStmt | Block | IfStmt | ElseStmt | ElifStmt | FuncDecl | Param | @@ -218,6 +250,7 @@ struct FuncDecl { params []Param ret_type string block Block + class_name ?string //if it is a method } struct ClassDecl { @@ -334,6 +367,8 @@ fn (mut p Parser) parse_primary() Expr { .lparen {p.parse_paren()} .kw_print {p.parse_print()} .plus, .minus {p.parse_unary_left(token.text)} + .kw_ref {p.parse_ref()} + .kw_deref {p.parse_deref()} else {parse_error("Unexpected Token")} } } @@ -368,13 +403,66 @@ fn (mut p Parser) parse_ident(ident string) Expr { } } -fn (mut p Parser) parse_member_access(from Expr) MemberAccess { +fn (mut p Parser) parse_ref() RefExpr { + p.expect(.lparen) + expr := p.parse_expr(.prefix) + match expr { + IntegerLiteral, StringLiteral, + RealLiteral, BoolLiteral {parse_error("Cannot get reference of literal value")} + else {} + } + p.expect(.rparen) + return RefExpr{expr: expr} +} + +fn (mut p Parser) parse_deref() DerefExpr { + p.expect(.lparen) + expr := p.parse_expr(.prefix) + if !p.get_expr_type(expr).contains('*') { + parse_error('cannot dereference a variable of type ${p.get_expr_type(expr)} (not a reference)') + } + p.expect(.rparen) + return DerefExpr{expr: expr} +} + +fn (mut p Parser) parse_member_access(from Expr) Expr { + if p.get_expr_type(from).count('*') > 1 { + base_class_name := p.get_expr_type(from).replace('*', '') + parse_error("Tried accessing member from object of type (${p.get_expr_type(from)}). \ + You can only access members from objects of type (${base_class_name}) \ + or (${base_class_name}*)") + } member_tok := p.next() if member_tok.type != .identifier {parse_error("Expected identifier after member access")} - from_class := p.symbols.lookup_class(p.get_expr_type(from)) or {panic("Accessing member from non-class type")} + base_class_name := p.get_expr_type(from).replace('*', '') + from_class := p.symbols.lookup_class(base_class_name) or {parse_error("Accessing member from non-class type")} + + if p.peek().type == .lparen { + if p.get_expr_type(from).contains('*') { + parse_error("Cannot call method on a reference") + } + methodinfo := p.symbols.lookup_method(member_tok.text, base_class_name) or { + dump(p.symbols) + parse_error("Calling undefined method ${member_tok.text}") + } + call := p.parse_call(member_tok.text) + return MethodCall{ + from: from + from_type: p.get_expr_type(from) + method: member_tok.text + ret_type: methodinfo.type + args: call.args + } + } + if !(member_tok.text in from_class.members_info) {panic("Accessing undefined member ${member_tok.text}")} member_type := from_class.members_info[member_tok.text].type - return MemberAccess {from: from, member: member_tok.text, member_type: member_type} + return MemberAccess { + from: from, + from_type: p.get_expr_type(from) + member: member_tok.text, + member_type: member_type + } } fn (mut p Parser) parse_class_member() ClassMember { @@ -386,17 +474,20 @@ fn (mut p Parser) parse_class_member() ClassMember { member_name := p.peek().text p.expect(.identifier) - type_tok := p.peek() + type_tok := p.next() type_name := match type_tok.type { .type {type_tok.text} .identifier { p.expect_ident_is_class(type_tok.text) type_tok.text } + .kw_ref { + ref_expr := p.parse_ref() + p.get_expr_type(ref_expr) + } else{parse_error("Expected type after class member ${member_name} in declaration, got ${p.peek().type}")} } - p.next() p.expect(.semicolon) return ClassMember{name: member_name, type: type_name is_immutable: is_immut} } @@ -531,7 +622,8 @@ fn (mut p Parser) get_expr_is_immutable(expr Expr) bool { UnaryExpr {p.get_expr_is_immutable(expr.expr)} TypeCast {p.get_expr_is_immutable(expr.expr)} MemberAccess { - classinfo := p.symbols.lookup_class(p.get_expr_type(expr.from)) or {parse_error("Invalid class")} + clean_class_name := p.get_expr_type(expr.from).replace('*', '') + classinfo := p.symbols.lookup_class(clean_class_name) or {parse_error("Invalid class")} memberinfo := classinfo.members_info[expr.member] or {parse_error("Undefined member ${expr.member}")} memberinfo.is_immutable } @@ -545,6 +637,11 @@ fn (mut p Parser) get_expr_is_immutable(expr Expr) bool { fn (mut p Parser) get_expr_type(expr Expr) string { return match expr { + RefExpr {p.get_expr_type(expr.expr)+'*'} + DerefExpr { + exprtype := p.get_expr_type(expr.expr) + exprtype[..exprtype.len-1] + } ParenExpr {p.get_expr_type(expr.expr)} IntegerLiteral {'int'} RealLiteral {'real'} @@ -569,10 +666,16 @@ fn (mut p Parser) get_expr_type(expr Expr) string { return info.type } TypeCast {expr.type} - FnCall { + FnCall { fninfo := p.symbols.lookup_func(expr.name) or {parse_error("Tried to call undefined function ${expr.name}")} fninfo.type } + MethodCall { + methodinfo := p.symbols.lookup_method(expr.method, p.get_expr_type(expr.from)) or { + parse_error("Tried to call undefined method ${expr.method}") + } + methodinfo.type + } MemberAccess { expr.member_type } @@ -643,9 +746,13 @@ fn (mut p Parser) parse_var_decl(is_const bool) VarDecl { type_name := match type_tok.type { .type {type_tok.text} .identifier { - p.expect_ident_is_class(type_tok.text) - type_tok.text - } + p.expect_ident_is_class(type_tok.text) + type_tok.text + } + .kw_ref { + ref_expr := p.parse_ref() + p.get_expr_type(ref_expr) + } else{parse_error("Expected variable type after name when declaring ${name_tok.text}")} } @@ -655,12 +762,12 @@ fn (mut p Parser) parse_var_decl(is_const bool) VarDecl { if type_tok.text == 'void' { parse_error("Cannot declare a variable of type void") } - if p.get_expr_type(val) != type_tok.text { - parse_error("Mismatch between declared type (${type_tok.text}) and actual type (${p.get_expr_type(val)})") + if p.get_expr_type(val) != type_name { + parse_error("Mismatch between declared type (${type_name}) and actual type (${p.get_expr_type(val)})") } p.expect(.semicolon) - p.symbols.define_var(name_tok.text, type_tok.text, is_const) + p.symbols.define_var(name_tok.text, type_name, is_const) return VarDecl { name: name_tok.text @@ -676,7 +783,17 @@ fn (mut p Parser) parse_func_decl() FuncDecl { p.expect(.kw_fn) + mut class_name := ?string(none) + if p.peek().type == .lparen { + p.next() + classname_tok := p.peek() + p.expect(.identifier) + class_name = classname_tok.text + p.expect(.rparen) + } + name_tok := p.next() + if name_tok.type != .identifier { parse_error("Expected function name after let") } @@ -689,20 +806,26 @@ fn (mut p Parser) parse_func_decl() FuncDecl { if p.peek().type != .rparen { for { - if p.peek().type != .identifier {parse_error("Invalid syntax to declare function arguments! use f(myint int, myreal real)")} + if p.peek().type != .identifier { + parse_error("Invalid syntax to declare function arguments!\ + use f(myint int, myreal real)") + } p_name := p.next().text - //if p.peek().type != .type {parse_error("Invalid syntax to declare function arguments! use f(myint int, myreal real)")} - type_tok := p.peek() + type_tok := p.next() p_type := match type_tok.type { .type {type_tok.text} .identifier { p.expect_ident_is_class(type_tok.text) type_tok.text - } - else{parse_error("Expected argument type after name when declaring ${p_name}")} + } + .kw_ref { + ref_expr := p.parse_ref() + dump(p.peek()) + p.get_expr_type(ref_expr) + } + else{parse_error("Expected argument type after name when declaring ${p_name}")} } - p.next() params << Param{p_name, p_type} p.symbols.define_var(p_name, p_type, false) @@ -716,19 +839,27 @@ fn (mut p Parser) parse_func_decl() FuncDecl { p.expect(.rparen) - p.dump_token() - type_tok := p.next() ret_type := match type_tok.type { .type {type_tok.text} .identifier { p.expect_ident_is_class(type_tok.text) type_tok.text - } - else{parse_error("Expected function return type after name when declaring ${name_tok.text}")} + } + .kw_ref { + ref_expr := p.parse_ref() + p.get_expr_type(ref_expr) + } + else{parse_error("Expected function return type after name when declaring ${name_tok.text}")} } - p.symbols.define_func(name_tok.text, type_tok.text, Block{}) + if class_name != none { + p.symbols.define_method(name_tok.text, class_name, type_tok.text, Block{}) + p.symbols.define_var('this', class_name+'*', false) + params << Param{'this', class_name+'*'} + } else { + p.symbols.define_func(name_tok.text, type_tok.text, Block{}) + } block := p.parse_block(true) @@ -743,13 +874,18 @@ fn (mut p Parser) parse_func_decl() FuncDecl { p.symbols.variable_scopes.delete_last() - p.symbols.define_func(name_tok.text, type_tok.text, block) + if class_name != none { + p.symbols.define_method(name_tok.text, class_name, type_tok.text, block) + } else { + p.symbols.define_func(name_tok.text, type_tok.text, block) + } return FuncDecl { name: name_tok.text ret_type: ret_type block: block params: params + class_name: class_name } }