Files
one/parser.v

1031 lines
22 KiB
V

module main
import term
// ------------------------------------------- Precedence
enum Precedence {
base
assignment // = , +=, -=
comparison // ==, !=, <, >
sum // +, -
product // *, /
prefix // -x, !x
suffix // ++, --
call // function()
access // .
}
fn (p Parser) get_precedence(tok_type TokenType) Precedence {
return match tok_type {
.equals, .plus_eq, .minus_eq, .star_eq, .slash_eq, .percent_eq, .and_eq, .or_eq, .and_and, .or_or {.assignment}
.eq_eq, .not_eq, .less_eq, .greater_eq, .less, .greater {.comparison}
.plus, .minus {.sum}
.star, .slash, .percent {.product}
.dot {.access}
.increment, .decrement {.suffix}
else {.base}
}
}
// ------------------------------------------- Symbol Table
type SymbolInfo = VarSymbolInfo | FuncSymbolInfo | ClassSymbolInfo
struct VarSymbolInfo {
type string
is_immutable bool
}
struct FuncSymbolInfo {
type string
block Block
}
struct ClassSymbolInfo {
name string
members_info map[string]VarSymbolInfo
mut:
methods_info map[string]FuncSymbolInfo
}
struct SymbolTable {
mut:
variable_scopes []map[string]VarSymbolInfo
functions map[string]FuncSymbolInfo
structs map[string]ClassSymbolInfo
}
fn (mut s SymbolTable) define_var(name string, typ string, is_const bool) {
$if debug {
dump(s.variable_scopes.len)
}
if s.variable_scopes.len == 0 {
parse_error('No scope available')
}
if name in s.variable_scopes[s.variable_scopes.len-1] {
parse_error('Variable ${name} already defined in this scope')
}
s.variable_scopes[s.variable_scopes.len-1][name] = VarSymbolInfo{
type: typ
is_immutable: is_const
}
}
fn (mut s SymbolTable) lookup_var(name string) ?VarSymbolInfo {
if s.variable_scopes.len == 0 {return none}
for variables in s.variable_scopes.reverse() {
if name in variables {
return variables[name]
}
}
return none
}
fn (mut s SymbolTable) define_func(name string, typ string, block Block) {
s.functions[name] = FuncSymbolInfo{type: typ, block: block}
}
fn (mut s SymbolTable) lookup_func(name string) ?FuncSymbolInfo {
if name in s.functions {
return s.functions[name]
}
return none
}
fn (mut s SymbolTable) define_class(name string, members []ClassMember) {
mut members_info := map[string]VarSymbolInfo{}
for member in members {
members_info[member.name] = VarSymbolInfo{
type: member.type,
is_immutable: member.is_immutable
}
}
s.structs[name] = ClassSymbolInfo{name: name, members_info: members_info}
}
fn (mut s SymbolTable) lookup_class(name string) ?ClassSymbolInfo {
if name in s.structs {
return s.structs[name]
}
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
}
// ------------------------------------------- Expressions
type Expr = VoidExpr | UnaryExpr | BinaryExpr | IntegerLiteral | RealLiteral | BoolLiteral | StringLiteral |
Variable | TypeExpr | Function | TypeCast | ParenExpr | PrintExpr | FnCall | ClassMember |
ClassInstantiation | MemberAccess | MethodCall | RefExpr | DerefExpr
struct VoidExpr {}
struct RefExpr {
expr Expr
}
struct DerefExpr {
expr Expr
}
struct UnaryExpr {
expr Expr
op string
op_on_left bool
}
struct BinaryExpr {
left Expr
op string
right Expr
}
struct IntegerLiteral {
val i32
}
struct RealLiteral {
val f32
}
struct BoolLiteral {
val bool
}
struct StringLiteral {
val string
}
struct Variable {
name string
}
struct TypeExpr {
name string
}
struct ClassMember {
name string
type string
is_immutable bool
}
struct MemberAccess {
from Expr
from_type string
member string
member_type string
}
struct ClassInstantiation {
name string
member_values []Expr
}
struct Function {
name string
}
struct TypeCast {
type string
expr Expr
}
struct ParenExpr {
expr Expr
}
struct PrintExpr {
exprs []Expr
types []string
}
struct FnCall {
name string
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 |
ClassDecl | WhileLoop
struct VarDecl {
name string
type string
value Expr
const bool
}
struct FuncDecl {
name string
params []Param
ret_type string
block Block
class_name ?string //if it is a method
}
struct ClassDecl {
name string
members []ClassMember
}
struct ExprStmt {
expr Expr
}
struct ReturnStmt {
expr Expr
}
struct Block {
stmts []Stmt
}
struct IfStmt {
condition Expr
block Block
}
struct ElseStmt {
block Block
}
struct ElifStmt {
condition Expr
block Block
}
struct WhileLoop {
guard Expr
block Block
}
struct Param {
name string
type string
}
// ------------------------------------------- Parser
struct Parser {
tokens []Token
mut:
symbols SymbolTable
pos int
line int
statements []Stmt
inside_struct bool
}
fn (mut p Parser) peek() Token {
return p.tokens[p.pos]
}
fn (mut p Parser) next() Token {
token := p.tokens[p.pos]
p.pos++
return token
}
fn (mut p Parser) expect(type TokenType) {
if p.peek().type != type {
parse_error('Expected ${str_from_toktype(type)}, got ${str_from_toktype(p.peek().type)}')
}
p.next()
}
fn (mut p Parser) expect_ident_is_class(ident string) {
if !p.is_ident_class(ident) {panic("Expected type or class type but got identifier")}
}
// ------------------------------------------- Debug
fn (mut p Parser) dump_token() {
$if debug {
dump(p.peek())
}
}
fn (mut p Parser) dump_stmt() {
$if debug {
if p.statements.len > 0 {
dump(p.statements[p.statements.len-1])
}
}
}
@[noreturn]
fn parse_error(str string) {
eprintln(term.red("Parse Error: ${str}"))
panic("")
}
// ------------------------------------------- Expressions
fn (mut p Parser) parse_primary() Expr {
token := p.next()
p.dump_token()
return match token.type {
.integer {IntegerLiteral{token.text.int()}}
.real {RealLiteral{token.text.f32()}}
.boolean {BoolLiteral{token.text == 'true'}}
.string {StringLiteral{token.text}}
.kw_fn {Function{token.text}}
.identifier {p.parse_ident(token.text)}
.type {p.parse_type(token.text)}
.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")}
}
}
fn (mut p Parser) parse_expr(prec Precedence) Expr {
mut expr := p.parse_primary()
for int(prec) < int(p.get_precedence(p.peek().type)) {
op_tok := p.next()
expr = match op_tok.type {
.dot {p.parse_member_access(expr)}
.increment, .decrement {p.parse_unary_right(expr, op_tok.text)}
else {p.parse_binary(expr, op_tok.text, p.get_precedence(op_tok.type))}
}
}
return expr
}
fn (mut p Parser) parse_ident(ident string) Expr {
if p.symbols.lookup_class(ident) != none {
return match p.peek().type {
.lbracket {p.parse_class_inst(ident)}
else {p.parse_type('${ident}')}
}
}
return match p.peek().type {
.lparen {p.parse_call(ident)}
else {Variable{ident}}
}
}
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")}
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,
from_type: p.get_expr_type(from)
member: member_tok.text,
member_type: member_type
}
}
fn (mut p Parser) parse_class_member() ClassMember {
mut is_immut := false
if p.peek().type == .kw_immutable {
is_immut = true
p.next()
}
member_name := p.peek().text
p.expect(.identifier)
p.expect(.colon)
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.expect(.semicolon)
return ClassMember{name: member_name, type: type_name is_immutable: is_immut}
}
fn (mut p Parser) parse_class_inst(name string) ClassInstantiation {
p.expect(.lbracket)
mut member_values := []Expr{}
if p.peek().type != .rbracket {
for {
member_values << p.parse_expr(.base)
if p.peek().type == .comma {
p.next()
} else {
break
}
}
}
p.expect(.rbracket)
return ClassInstantiation{name: name, member_values: member_values}
}
fn (mut p Parser) parse_call(name string) FnCall {
p.expect(.lparen)
mut args := []Expr{}
if p.peek().type != .rparen {
for {
args << p.parse_expr(.base)
if p.peek().type == .comma {
p.next()
} else {
break
}
}
}
p.expect(.rparen)
return FnCall{name: name, args: args}
}
fn (mut p Parser) parse_print() PrintExpr {
p.expect(.lparen)
mut exprs := []Expr{}
mut types := []string{}
for p.peek().type != .rparen {
expr := p.parse_expr(.base)
exprs << expr
types << p.get_expr_type(expr)
if p.peek().type == .comma {
p.next()
} else {
break
}
}
p.expect(.rparen)
return PrintExpr{exprs: exprs, types: types}
}
fn (mut p Parser) parse_unary_right(expr Expr, op string) UnaryExpr {
if p.get_expr_is_immutable(expr) {
parse_error("Cannot perform operation ${op} on immutable expression ${expr}")
}
if !p.is_op_valid_for_type(p.get_expr_type(expr), op) {
parse_error("Invalid operation ${op} for type ${p.get_expr_type(expr)}")
}
return UnaryExpr {expr: expr, op: op, op_on_left: false}
}
fn (mut p Parser) parse_unary_left(op string) UnaryExpr {
expr := p.parse_expr(.prefix)
return UnaryExpr{expr: expr, op: op, op_on_left: true}
}
fn (mut p Parser) parse_binary(left Expr, op string, prec Precedence) BinaryExpr {
if op in ['=', '+=', '-=', '*=', '/=', '%='] {
if p.get_expr_is_immutable(left) {
parse_error("Cannot assign to immutable expression ${left}")
}
}
dump(op)
right := p.parse_expr(prec)
binary_expr := BinaryExpr{left, op, right}
if !p.is_op_valid_for_type(p.get_expr_type(left), op) {
parse_error("Illegal operation ${op} for type ${p.get_expr_type(left)}")
}
p.check_binary_expr_types(binary_expr)
return binary_expr
}
fn (mut p Parser) parse_type(type string) Expr {
if p.peek().type == .lparen {
p.next()
expr := p.parse_expr(.base)
p.expect(.rparen)
return TypeCast {
expr: expr
type: type
}
}
return TypeExpr {name: type}
}
fn (mut p Parser) parse_paren() ParenExpr {
expr := p.parse_expr(.base)
p.expect(.rparen)
return ParenExpr{expr: expr}
}
fn (mut p Parser) check_binary_expr_types(expr BinaryExpr) {
left_t := p.get_expr_type(expr.left)
right_t := p.get_expr_type(expr.right)
dump("${left_t}, ${right_t}")
if left_t != right_t {
parse_error('Type mismatch in expression: ${left_t} and ${right_t}')
}
}
fn (mut p Parser) is_ident_class(ident string) bool {
return p.symbols.lookup_class(ident) != none
}
fn (mut p Parser) get_expr_is_immutable(expr Expr) bool {
return match expr {
ParenExpr {p.get_expr_is_immutable(expr.expr)}
UnaryExpr {p.get_expr_is_immutable(expr.expr)}
TypeCast {p.get_expr_is_immutable(expr.expr)}
MemberAccess {
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
}
Variable {
varinfo := p.symbols.lookup_var(expr.name) or {parse_error("Undefined variable ${expr.name}")}
varinfo.is_immutable
}
else {true}
}
}
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'}
BoolLiteral {'bool'}
StringLiteral {'string'}
VoidExpr {'void'}
TypeExpr {expr.name}
UnaryExpr {p.get_expr_type(expr.expr)}
BinaryExpr {
p.check_binary_expr_types(expr)
left_t := p.get_expr_type(expr.left)
if expr.op in ['<=', '==', '>=', '!=', '<', '>', '||', '&&'] {
'bool'
} else {
left_t
}
}
Variable {
info := p.symbols.lookup_var(expr.name) or {
parse_error("Undefined variable ${expr.name}")
}
return info.type
}
TypeCast {expr.type}
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
}
ClassInstantiation {expr.name}
else {"Tried getting type of unexpected Expr"}
}
}
fn (mut p Parser) is_op_valid_for_type(type string, op string) bool {
global := ['=', '==', '!=']
mut legal_ops := match type {
'int' {[
'+', '-', '*', '/', '%', '<', '>', '<=',
'>=', '++', '--', '+=', '-=', '*=', '/=',
'%='
]}
'real' {[
'+', '-', '*', '/', '<', '>', '<=', '>=',
'++', '--', '+=', '-=', '*=', '/=',
]}
'bool' {['&&', '||', '&', '|', '&=', '|=']}
else {[]}
}
legal_ops << global
return op in legal_ops
}
// ------------------------------------------- Statements
fn (mut p Parser) get_return_stmts_recursive(block Block) []ReturnStmt {
mut returns := []ReturnStmt{}
for stmt in block.stmts {
if stmt is ReturnStmt {
returns << stmt
}
if stmt is Block {
returns << p.get_return_stmts_recursive(stmt)
}
}
return returns
}
fn (mut p Parser) parse_statement() Stmt {
match p.peek().type {
.kw_let {return p.parse_var_decl(false)}
.kw_const {return p.parse_var_decl(true)}
.kw_return {return p.parse_return_stmt()}
.kw_fn {return p.parse_func_decl()}
.lbracket {return p.parse_block(false)}
.kw_class {return p.parse_class()}
.kw_if {return p.parse_if()}
.kw_else {return p.parse_else()}
.kw_elif {return p.parse_elif()}
.kw_while {return p.parse_while()}
else {return p.parse_expr_stmt()}
}
}
fn (mut p Parser) parse_var_decl(is_const bool) VarDecl {
p.next()
name_tok := p.next()
if name_tok.type != .identifier {
parse_error("Expected variable name after let")
}
p.expect(.colon)
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 variable type after name when declaring ${name_tok.text}")}
}
p.expect(.equals)
val := p.parse_expr(.base)
if type_tok.text == 'void' {
parse_error("Cannot declare a variable of type void")
}
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_name, is_const)
return VarDecl {
name: name_tok.text
value: val
type: type_name
const: is_const
}
}
fn (mut p Parser) parse_func_decl() FuncDecl {
if !p.symbols.is_in_global_scope() {parse_error("Tried to define a function in a non-global scope")}
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")
}
p.expect(.lparen)
mut params := []Param{}
p.symbols.variable_scopes << map[string]VarSymbolInfo{}
if class_name != none {
params << Param{'this', class_name+'*'}
}
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)")
}
p_name := p.next().text
p.expect(.colon)
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}")}
}
params << Param{p_name, p_type}
p.symbols.define_var(p_name, p_type, false)
if p.peek().type == .comma {
p.next()
} else {
break
}
}
}
p.expect(.rparen)
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
}
.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)
} else {
p.symbols.define_func(name_tok.text, type_tok.text, Block{})
}
block := p.parse_block(true)
return_stmts := p.get_return_stmts_recursive(block)
for return_stmt in return_stmts {
if p.get_expr_type(return_stmt.expr) != type_tok.text {
parse_error("Mismatch between declared return type (${type_tok.text}) \
and actual return type (${p.get_expr_type(return_stmt.expr)})")
}
}
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
}
}
fn (mut p Parser) parse_class() ClassDecl {
p.expect(.kw_class)
name := p.peek().text
p.expect(.identifier)
p.expect(.lbracket)
mut members := []ClassMember{}
for p.peek().type != .rbracket {
members << p.parse_class_member()
}
p.expect(.rbracket)
p.symbols.define_class(name, members)
return ClassDecl{name: name, members: members}
}
fn (mut p Parser) parse_return_stmt() ReturnStmt {
p.expect(.kw_return)
expr := p.parse_expr(.base)
p.expect(.semicolon)
return ReturnStmt {
expr: expr
}
}
fn (mut p Parser) parse_while() WhileLoop {
p.expect(.kw_while)
cond := p.parse_expr(.base)
if p.get_expr_type(cond) != 'bool' {
parse_error('While loop guard must be of type bool')
}
block := p.parse_block(false)
return WhileLoop {cond, block}
}
fn (mut p Parser) parse_if() IfStmt {
p.expect(.kw_if)
cond := p.parse_expr(.base)
if p.get_expr_type(cond) != 'bool' {
parse_error('If condition must be of type bool')
}
block := p.parse_block(false)
return IfStmt {cond, block}
}
fn (mut p Parser) parse_else() ElseStmt {
p.expect(.kw_else)
block := p.parse_block(false)
return ElseStmt {block}
}
fn (mut p Parser) parse_elif() ElifStmt {
p.expect(.kw_elif)
cond := p.parse_expr(.base)
if p.get_expr_type(cond) != 'bool' {
parse_error('If condition must be of type bool')
}
block := p.parse_block(false)
return ElifStmt {cond, block}
}
fn (mut p Parser) parse_expr_stmt() ExprStmt {
expr := p.parse_expr(.base)
p.expect(.semicolon)
return ExprStmt {
expr: expr
}
}
fn (mut p Parser) parse_block(no_scope bool) Block {
p.expect(.lbracket)
mut statements := []Stmt{}
if !no_scope {
p.symbols.variable_scopes << map[string]VarSymbolInfo{}
}
$if debug {
println("entering scope")
}
for p.peek().type != .rbracket && p.peek().type != .eof {
statements << p.parse_statement()
}
p.expect(.rbracket)
return_stmts := (statements.filter(it is ReturnStmt).map(it as ReturnStmt))
if return_stmts.len > 0 && (return_stmts.len > 1 || Stmt(return_stmts[0]) != statements[statements.len - 1]) {
parse_error("Unexpected use of return. Unreachable code")
}
if !no_scope {
p.symbols.variable_scopes.delete_last()
}
$if debug {
println("exiting scope")
}
return Block {
stmts: statements
}
}
fn (mut p Parser) parse_program() ([]Stmt, SymbolTable) {
p.symbols.define_class('string',[
ClassMember{'len', 'int', true}
])
p.symbols.variable_scopes << map[string]VarSymbolInfo{}
for p.peek().type != .eof {
p.statements << p.parse_statement()
}
p.symbols.variable_scopes.delete_last()
$if debug {
dump(p.symbols.functions)
}
return p.statements, p.symbols
}