865 lines
18 KiB
V
865 lines
18 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 { .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
|
|
}
|
|
|
|
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 debug {
|
|
dump(s.variable_scopes.len)
|
|
}
|
|
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) 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
|
|
|
|
struct VoidExpr {}
|
|
|
|
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
|
|
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
|
|
}
|
|
|
|
// ------------------------------------------- Statements
|
|
|
|
type Stmt = VarDecl | ExprStmt | ReturnStmt | Block | IfStmt | ElseStmt | ElifStmt | FuncDecl | Param | ClassDecl
|
|
|
|
struct VarDecl {
|
|
name string
|
|
type string
|
|
value Expr
|
|
const bool
|
|
}
|
|
|
|
struct FuncDecl {
|
|
name string
|
|
params []Param
|
|
ret_type string
|
|
block Block
|
|
}
|
|
|
|
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 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)}
|
|
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_member_access(from Expr) MemberAccess {
|
|
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")}
|
|
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}
|
|
}
|
|
|
|
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)
|
|
|
|
type_tok := p.peek()
|
|
type_name := match type_tok.type {
|
|
.type {type_tok.text}
|
|
.identifier {
|
|
p.expect_ident_is_class(type_tok.text)
|
|
type_tok.text
|
|
}
|
|
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}
|
|
}
|
|
|
|
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)
|
|
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 {
|
|
classinfo := p.symbols.lookup_class(p.get_expr_type(expr.from)) or {parse_error("Invalid class")}
|
|
memberinfo := classinfo.members_info[expr.member] or {parse_error("Undefined member ${expr.member}")}
|
|
memberinfo.is_immutable
|
|
}
|
|
else {true}
|
|
}
|
|
}
|
|
|
|
fn (mut p Parser) get_expr_type(expr Expr) string {
|
|
return match expr {
|
|
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
|
|
}
|
|
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()}
|
|
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")
|
|
}
|
|
|
|
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
|
|
}
|
|
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_tok.text {
|
|
parse_error("Mismatch between declared type (${type_tok.text}) and actual type (${p.get_expr_type(val)})")
|
|
}
|
|
p.expect(.semicolon)
|
|
|
|
p.symbols.define_var(name_tok.text, type_tok.text, 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)
|
|
|
|
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 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
|
|
//if p.peek().type != .type {parse_error("Invalid syntax to declare function arguments! use f(myint int, myreal real)")}
|
|
|
|
type_tok := p.peek()
|
|
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}")}
|
|
}
|
|
p.next()
|
|
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)
|
|
|
|
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}")}
|
|
}
|
|
|
|
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()
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
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_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
|
|
}
|
|
|
|
|