referencing and methods

This commit is contained in:
uan
2026-02-07 19:14:48 +01:00
parent 005828cec2
commit a3b978a09d
5 changed files with 269 additions and 39 deletions

View File

@@ -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
Functions in One are very similar to what you see in languages such as go. 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 ### Print
Printing is still a work in progress feature, but right now you can print any primitive and non-primitive type. Printing is still a work in progress feature, but right now you can print any primitive and non-primitive type.

View File

@@ -14,18 +14,24 @@ fn mangle_var(name string) string {
return 'one_var_${name}' return 'one_var_${name}'
} }
fn mangle_func(name string) string { fn mangle_func(name string, class_name ?string) string {
if name == 'main' {return 'main'} 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 { fn mangle_struct(name string) string {
return 'one_class_${name}_' return 'one_class_${name}'
} }
fn get_type_format(type string) string { fn get_type_format(type string) string {
dump(type) if type.contains('*') {
return 'p'
}
return match type { return match type {
'int', 'bool' {'d'} 'int', 'bool' {'d'}
'real' {'f'} '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 { fn (mut g Generator) get_c_type(typ string) string {
return match typ { astkcount := typ.count('*')
clean := typ.replace('*', '')
return match clean {
'real' {'float'} 'real' {'float'}
'int' {'int32_t'} 'int' {'int32_t'}
'string' {'OneString'} 'string' {'OneString'}
else {typ} else {clean}
} } + '*'.repeat(astkcount)
} }
fn (mut g Generator) mangle_if_class(name string) string { fn (mut g Generator) mangle_if_class(name string) string {
if g.symbols.lookup_class(name) != none { astkcount := name.count('*')
return 'struct ${mangle_struct(name)}' noptr := name.replace('*', '')
if g.symbols.lookup_class(noptr) != none {
return 'struct ${mangle_struct(noptr)}' + '*'.repeat(astkcount)
} }
return name return name
} }
@@ -119,7 +129,7 @@ fn (mut g Generator) gen_stmt(stmt Stmt) {
} }
FuncDecl { FuncDecl {
c_type := g.mangle_if_class(g.get_c_type(stmt.ret_type)) 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 { for param in stmt.params {
g.gen_stmt(param) g.gen_stmt(param)
if param != stmt.params[stmt.params.len-1]{ 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) { fn (mut g Generator) gen_expr(expr Expr) {
match 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 { RealLiteral {
g.out.write_string(expr.val.str()) g.out.write_string(expr.val.str())
} }
@@ -243,7 +263,22 @@ fn (mut g Generator) gen_expr(expr Expr) {
g.out.write_string(')') g.out.write_string(')')
} }
FnCall { 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 { for arg in expr.args {
g.gen_expr(arg) g.gen_expr(arg)
if arg != expr.args[expr.args.len-1]{ if arg != expr.args[expr.args.len-1]{
@@ -268,7 +303,12 @@ fn (mut g Generator) gen_expr(expr Expr) {
} }
MemberAccess { MemberAccess {
g.gen_expr(expr.from) 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")} else {panic("Unimplemented expression")}
} }

View File

@@ -16,6 +16,8 @@ enum TokenType as u8 {
kw_return kw_return
kw_print kw_print
kw_class kw_class
kw_ref
kw_deref
integer integer
real real
boolean boolean
@@ -126,6 +128,8 @@ fn toktype_from_kw(kw string) TokenType {
'print' {.kw_print} 'print' {.kw_print}
'class' {.kw_class} 'class' {.kw_class}
'immutable' {.kw_immutable} 'immutable' {.kw_immutable}
'ref' {.kw_ref}
'deref' {.kw_deref}
else {.unknown} else {.unknown}
} }
} }
@@ -145,7 +149,7 @@ fn is_real(str string) bool {
fn is_keyword(str string) bool { fn is_keyword(str string) bool {
return [ 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) ].contains(str)
} }

BIN
one

Binary file not shown.

168
parser.v
View File

@@ -45,6 +45,8 @@ struct FuncSymbolInfo {
struct ClassSymbolInfo { struct ClassSymbolInfo {
name string name string
members_info map[string]VarSymbolInfo members_info map[string]VarSymbolInfo
mut:
methods_info map[string]FuncSymbolInfo
} }
struct SymbolTable { struct SymbolTable {
@@ -112,6 +114,19 @@ fn (mut s SymbolTable) lookup_class(name string) ?ClassSymbolInfo {
return none 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 { fn (mut s SymbolTable) is_in_global_scope() bool {
return s.variable_scopes.len == 1 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 | type Expr = VoidExpr | UnaryExpr | BinaryExpr | IntegerLiteral | RealLiteral | BoolLiteral | StringLiteral |
Variable | TypeExpr | Function | TypeCast | ParenExpr | PrintExpr | FnCall | ClassMember | Variable | TypeExpr | Function | TypeCast | ParenExpr | PrintExpr | FnCall | ClassMember |
ClassInstantiation | MemberAccess ClassInstantiation | MemberAccess | MethodCall | RefExpr | DerefExpr
struct VoidExpr {} struct VoidExpr {}
struct RefExpr {
expr Expr
}
struct DerefExpr {
expr Expr
}
struct UnaryExpr { struct UnaryExpr {
expr Expr expr Expr
op string op string
@@ -169,6 +192,7 @@ struct ClassMember {
struct MemberAccess { struct MemberAccess {
from Expr from Expr
from_type string
member string member string
member_type string member_type string
} }
@@ -201,6 +225,14 @@ struct FnCall {
args []Expr args []Expr
} }
struct MethodCall {
from Expr
from_type string
method string
ret_type string
args []Expr
}
// ------------------------------------------- Statements // ------------------------------------------- Statements
type Stmt = VarDecl | ExprStmt | ReturnStmt | Block | IfStmt | ElseStmt | ElifStmt | FuncDecl | Param | type Stmt = VarDecl | ExprStmt | ReturnStmt | Block | IfStmt | ElseStmt | ElifStmt | FuncDecl | Param |
@@ -218,6 +250,7 @@ struct FuncDecl {
params []Param params []Param
ret_type string ret_type string
block Block block Block
class_name ?string //if it is a method
} }
struct ClassDecl { struct ClassDecl {
@@ -334,6 +367,8 @@ fn (mut p Parser) parse_primary() Expr {
.lparen {p.parse_paren()} .lparen {p.parse_paren()}
.kw_print {p.parse_print()} .kw_print {p.parse_print()}
.plus, .minus {p.parse_unary_left(token.text)} .plus, .minus {p.parse_unary_left(token.text)}
.kw_ref {p.parse_ref()}
.kw_deref {p.parse_deref()}
else {parse_error("Unexpected Token")} 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() member_tok := p.next()
if member_tok.type != .identifier {parse_error("Expected identifier after member access")} 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}")} 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 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 { 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 member_name := p.peek().text
p.expect(.identifier) p.expect(.identifier)
type_tok := p.peek() type_tok := p.next()
type_name := match type_tok.type { type_name := match type_tok.type {
.type {type_tok.text} .type {type_tok.text}
.identifier { .identifier {
p.expect_ident_is_class(type_tok.text) p.expect_ident_is_class(type_tok.text)
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}")} else{parse_error("Expected type after class member ${member_name} in declaration, got ${p.peek().type}")}
} }
p.next()
p.expect(.semicolon) p.expect(.semicolon)
return ClassMember{name: member_name, type: type_name is_immutable: is_immut} 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)} UnaryExpr {p.get_expr_is_immutable(expr.expr)}
TypeCast {p.get_expr_is_immutable(expr.expr)} TypeCast {p.get_expr_is_immutable(expr.expr)}
MemberAccess { 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 := classinfo.members_info[expr.member] or {parse_error("Undefined member ${expr.member}")}
memberinfo.is_immutable 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 { fn (mut p Parser) get_expr_type(expr Expr) string {
return match expr { 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)} ParenExpr {p.get_expr_type(expr.expr)}
IntegerLiteral {'int'} IntegerLiteral {'int'}
RealLiteral {'real'} RealLiteral {'real'}
@@ -573,6 +670,12 @@ fn (mut p Parser) get_expr_type(expr Expr) string {
fninfo := p.symbols.lookup_func(expr.name) or {parse_error("Tried to call undefined function ${expr.name}")} fninfo := p.symbols.lookup_func(expr.name) or {parse_error("Tried to call undefined function ${expr.name}")}
fninfo.type 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 { MemberAccess {
expr.member_type expr.member_type
} }
@@ -646,6 +749,10 @@ fn (mut p Parser) parse_var_decl(is_const bool) VarDecl {
p.expect_ident_is_class(type_tok.text) p.expect_ident_is_class(type_tok.text)
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}")} 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' { if type_tok.text == 'void' {
parse_error("Cannot declare a variable of type void") parse_error("Cannot declare a variable of type void")
} }
if p.get_expr_type(val) != type_tok.text { if p.get_expr_type(val) != type_name {
parse_error("Mismatch between declared type (${type_tok.text}) and actual type (${p.get_expr_type(val)})") parse_error("Mismatch between declared type (${type_name}) and actual type (${p.get_expr_type(val)})")
} }
p.expect(.semicolon) 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 { return VarDecl {
name: name_tok.text name: name_tok.text
@@ -676,7 +783,17 @@ fn (mut p Parser) parse_func_decl() FuncDecl {
p.expect(.kw_fn) 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() name_tok := p.next()
if name_tok.type != .identifier { if name_tok.type != .identifier {
parse_error("Expected function name after let") parse_error("Expected function name after let")
} }
@@ -689,20 +806,26 @@ fn (mut p Parser) parse_func_decl() FuncDecl {
if p.peek().type != .rparen { if p.peek().type != .rparen {
for { 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 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 { p_type := match type_tok.type {
.type {type_tok.text} .type {type_tok.text}
.identifier { .identifier {
p.expect_ident_is_class(type_tok.text) p.expect_ident_is_class(type_tok.text)
type_tok.text type_tok.text
} }
.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}")} else{parse_error("Expected argument type after name when declaring ${p_name}")}
} }
p.next()
params << Param{p_name, p_type} params << Param{p_name, p_type}
p.symbols.define_var(p_name, p_type, false) p.symbols.define_var(p_name, p_type, false)
@@ -716,8 +839,6 @@ fn (mut p Parser) parse_func_decl() FuncDecl {
p.expect(.rparen) p.expect(.rparen)
p.dump_token()
type_tok := p.next() type_tok := p.next()
ret_type := match type_tok.type { ret_type := match type_tok.type {
.type {type_tok.text} .type {type_tok.text}
@@ -725,10 +846,20 @@ fn (mut p Parser) parse_func_decl() FuncDecl {
p.expect_ident_is_class(type_tok.text) p.expect_ident_is_class(type_tok.text)
type_tok.text type_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}")} else{parse_error("Expected function return type after name when declaring ${name_tok.text}")}
} }
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{}) p.symbols.define_func(name_tok.text, type_tok.text, Block{})
}
block := p.parse_block(true) 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.variable_scopes.delete_last()
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) p.symbols.define_func(name_tok.text, type_tok.text, block)
}
return FuncDecl { return FuncDecl {
name: name_tok.text name: name_tok.text
ret_type: ret_type ret_type: ret_type
block: block block: block
params: params params: params
class_name: class_name
} }
} }