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 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.

View File

@@ -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")}
}

View File

@@ -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)
}

BIN
one

Binary file not shown.

168
parser.v
View File

@@ -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'}
@@ -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.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
}
@@ -646,6 +749,10 @@ fn (mut p Parser) parse_var_decl(is_const bool) VarDecl {
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
}
.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,8 +839,6 @@ 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}
@@ -725,10 +846,20 @@ fn (mut p Parser) parse_func_decl() FuncDecl {
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 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{})
}
block := p.parse_block(true)
@@ -743,13 +874,18 @@ fn (mut p Parser) parse_func_decl() FuncDecl {
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)
}
return FuncDecl {
name: name_tok.text
ret_type: ret_type
block: block
params: params
class_name: class_name
}
}