Compare commits

...

2 Commits

5 changed files with 164 additions and 63 deletions

View File

@@ -86,6 +86,21 @@ get translated to during compilation. Comparisons and operations on strings are
and quite buggy, but you are free to use string literals for prints or simple variable declarations with no issues. and quite buggy, but you are free to use string literals for prints or simple variable declarations with no issues.
Variables cannot be declared with type `void`. Variables cannot be declared with type `void`.
The `string` type actually behaves similarly to how a class would. While using the variable directly
gives you access to the actual string (which in C corresponds to the `char*`), you can use `.len` to
get the length of the string. Note that `len` is an immutable member, so you cannot maually set it.
```
let mystring string = "this is my string";
print(mystring, mystring.len)
```
produces this output:
```
this is my string 17
```
--- ---
### Functions ### Functions
@@ -133,6 +148,26 @@ let myuser User = User{17, "uan", false}
Class types can be used anywhere primitive types can, such as function arguments Class types can be used anywhere primitive types can, such as function arguments
or other classes' members. or other classes' members.
Class members can be defined as immutable with the `immutable` keyword before the member name.
This is checked at compile time and prevents that member's value to be changed after instantiation.
For example, this code would trigger a parse error, `as u.id = 0` attempts to change an immutable
member's value.
```
class User {
age int
name string
mail_verified bool
immutable id int
}
fn change_values(u User) {
u.age = 10;
u.name = "new name";
u.id = 0;
}
```
--- ---
### Print ### Print

View File

@@ -24,6 +24,7 @@ fn mangle_struct(name string) string {
fn get_type_format(type string) string { fn get_type_format(type string) string {
dump(type)
return match type { return match type {
'int', 'bool' {'d'} 'int', 'bool' {'d'}
'real' {'f'} 'real' {'f'}
@@ -46,7 +47,7 @@ fn (mut g Generator) get_c_type(typ string) string {
return match typ { return match typ {
'real' {'float'} 'real' {'float'}
'int' {'int32_t'} 'int' {'int32_t'}
'string' {'char*'} 'string' {'OneString'}
else {typ} else {typ}
} }
} }
@@ -70,12 +71,16 @@ fn (mut g Generator) gen_class_print_func(stmt ClassDecl) {
g.out.writeln('for(int i=0; i<indent + 1; i++) printf(" ");') g.out.writeln('for(int i=0; i<indent + 1; i++) printf(" ");')
g.out.write_string('printf("${member.name}: ");') g.out.write_string('printf("${member.name}: ");')
if g.symbols.lookup_class(member.type) != none { if member.type != 'string' && g.symbols.lookup_class(member.type) != none {
inner_struct_name := mangle_struct(member.type) inner_struct_name := mangle_struct(member.type)
g.out.writeln('print_${inner_struct_name}(s.${member.name}, indent + 1);') g.out.writeln('print_${inner_struct_name}(s.${member.name}, indent + 1);')
} else { } else {
format := get_type_format(member.type) format := get_type_format(member.type)
g.out.writeln('printf("%${format}\\n", s.${member.name});') g.out.write_string('printf("%${format}\\n", s.${member.name}')
if member.type == 'string' {
g.out.write_string('.string')
}
g.out.writeln(');')
} }
} }
@@ -168,13 +173,21 @@ fn (mut g Generator) gen_expr(expr Expr) {
g.out.write_string(expr.val.str()) g.out.write_string(expr.val.str())
} }
StringLiteral { StringLiteral {
g.out.write_string('\"${expr.val}\"') g.out.write_string('(OneString){\"${expr.val}\", ${expr.val.len}}')
} }
Variable { Variable {
g.out.write_string(mangle_var(expr.name)) g.out.write_string(mangle_var(expr.name))
} }
UnaryExpr { UnaryExpr {
g.out.write_string('(${mangle_var(expr.ident)}${expr.op})') if expr.op_on_left {
g.out.write_string('(${expr.op}')
g.gen_expr(expr.expr)
g.out.write_string(')')
} else {
g.out.write_string('(')
g.gen_expr(expr.expr)
g.out.write_string('${expr.op})')
}
} }
BinaryExpr { BinaryExpr {
g.out.write_string('(') g.out.write_string('(')
@@ -188,16 +201,20 @@ fn (mut g Generator) gen_expr(expr Expr) {
for i < expr.exprs.len { for i < expr.exprs.len {
inner_expr := expr.exprs[i]; inner_expr := expr.exprs[i];
expr_type := expr.types[i]; expr_type := expr.types[i];
if g.symbols.lookup_class(expr_type) != none { if expr_type != 'string' && g.symbols.lookup_class(expr_type) != none {
class_name := mangle_struct(expr_type) class_name := mangle_struct(expr_type)
g.out.write_string('print_${class_name}(') g.out.write_string('print_${class_name}(')
g.gen_expr(inner_expr) g.gen_expr(inner_expr)
g.out.write_string(', 0);') g.out.write_string(', 0);')
} else {
g.out.write_string('printf(\"') g.out.write_string('printf(\"')
format := get_type_format(expr_type) format := get_type_format(expr_type)
g.out.write_string('%${format}') g.out.write_string('%${format}')
g.out.write_string('\", ') g.out.write_string('\", ')
g.gen_expr(inner_expr) g.gen_expr(inner_expr)
if expr_type == 'string' {
g.out.write_string('.string')
}
g.out.write_string(');') g.out.write_string(');')
} }
i++; i++;
@@ -254,7 +271,7 @@ fn (mut g Generator) gen_c(program []Stmt) string {
g.out.writeln('#include <stdio.h>') g.out.writeln('#include <stdio.h>')
g.out.writeln('#include <stdbool.h>') g.out.writeln('#include <stdbool.h>')
g.out.writeln('#include <stdint.h>') g.out.writeln('#include <stdint.h>')
//g.out.writeln('typedef struct __one_string_builtin__ {\nchar* string;\nint len;\n} string;') g.out.writeln('typedef struct {\nchar* string;\nint len;\n} OneString;')
for stmt in program { for stmt in program {
g.gen_stmt(stmt) g.gen_stmt(stmt)
} }

View File

@@ -5,6 +5,7 @@ import term
enum TokenType as u8 { enum TokenType as u8 {
kw_let kw_let
kw_const kw_const
kw_immutable
type type
kw_if kw_if
kw_else kw_else
@@ -108,6 +109,7 @@ fn toktype_from_kw(kw string) TokenType {
'true', 'false' {.boolean} 'true', 'false' {.boolean}
'print' {.kw_print} 'print' {.kw_print}
'class' {.kw_class} 'class' {.kw_class}
'immutable' {.kw_immutable}
else {.unknown} else {.unknown}
} }
} }
@@ -127,7 +129,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", "for", "break", "fn", "return", "let", "const", "true", "false", "print", "class" "void", "int", "real", "bool", "string", "if", "else", "elif", "for", "break", "fn", "return", "let", "const", "true", "false", "print", "class", "immutable"
].contains(str) ].contains(str)
} }

BIN
one

Binary file not shown.

133
parser.v
View File

@@ -5,12 +5,13 @@ import term
// ------------------------------------------- Precedence // ------------------------------------------- Precedence
enum Precedence { enum Precedence {
lowest base
assignment // = , +=, -= assignment // = , +=, -=
comparison // ==, !=, <, > comparison // ==, !=, <, >
sum // +, - sum // +, -
product // *, / product // *, /
prefix // -x, !x prefix // -x, !x
suffix // ++, --
call // function() call // function()
access // . access // .
} }
@@ -22,7 +23,8 @@ fn (p Parser) get_precedence(tok_type TokenType) Precedence {
.plus, .minus { .sum } .plus, .minus { .sum }
.star, .slash { .product } .star, .slash { .product }
.dot { .access } .dot { .access }
else { .lowest } .increment, .decrement { .suffix }
else { .base}
} }
} }
@@ -32,6 +34,7 @@ type SymbolInfo = VarSymbolInfo | FuncSymbolInfo | ClassSymbolInfo
struct VarSymbolInfo { struct VarSymbolInfo {
type string type string
is_immutable bool
} }
struct FuncSymbolInfo { struct FuncSymbolInfo {
@@ -51,7 +54,7 @@ mut:
structs map[string]ClassSymbolInfo structs map[string]ClassSymbolInfo
} }
fn (mut s SymbolTable) define_var(name string, typ string) { fn (mut s SymbolTable) define_var(name string, typ string, is_const bool) {
$if debug { $if debug {
dump(s.variable_scopes.len) dump(s.variable_scopes.len)
} }
@@ -66,6 +69,7 @@ fn (mut s SymbolTable) define_var(name string, typ string) {
s.variable_scopes[s.variable_scopes.len-1][name] = VarSymbolInfo{ s.variable_scopes[s.variable_scopes.len-1][name] = VarSymbolInfo{
type: typ type: typ
is_immutable: is_const
} }
} }
@@ -96,7 +100,10 @@ fn (mut s SymbolTable) lookup_func(name string) ?FuncSymbolInfo {
fn (mut s SymbolTable) define_class(name string, members []ClassMember) { fn (mut s SymbolTable) define_class(name string, members []ClassMember) {
mut members_info := map[string]VarSymbolInfo{} mut members_info := map[string]VarSymbolInfo{}
for member in members { for member in members {
members_info[member.name] = VarSymbolInfo{member.type} members_info[member.name] = VarSymbolInfo{
type: member.type,
is_immutable: member.is_immutable
}
} }
s.structs[name] = ClassSymbolInfo{name: name, members_info: members_info} s.structs[name] = ClassSymbolInfo{name: name, members_info: members_info}
} }
@@ -120,8 +127,9 @@ type Expr = VoidExpr | UnaryExpr | BinaryExpr | IntegerLiteral | RealLiteral | B
struct VoidExpr {} struct VoidExpr {}
struct UnaryExpr { struct UnaryExpr {
ident string expr Expr
op string op string
op_on_left bool
} }
struct BinaryExpr { struct BinaryExpr {
@@ -157,11 +165,13 @@ struct TypeExpr {
struct ClassMember { struct ClassMember {
name string name string
type string type string
is_immutable bool
} }
struct MemberAccess { struct MemberAccess {
from Expr from Expr
member string member string
member_type string
} }
struct ClassInstantiation { struct ClassInstantiation {
@@ -275,7 +285,6 @@ fn (mut p Parser) expect(type TokenType) {
p.next() p.next()
} }
fn (mut p Parser) expect_ident_is_class(ident string) { 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")} if !p.is_ident_class(ident) {panic("Expected type or class type but got identifier")}
} }
@@ -319,6 +328,7 @@ fn (mut p Parser) parse_primary() Expr {
.type {p.parse_type(token.text)} .type {p.parse_type(token.text)}
.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)}
else {parse_error("Unexpected Token")} else {parse_error("Unexpected Token")}
} }
} }
@@ -326,15 +336,13 @@ fn (mut p Parser) parse_primary() Expr {
fn (mut p Parser) parse_expr(prec Precedence) Expr { fn (mut p Parser) parse_expr(prec Precedence) Expr {
mut expr := p.parse_primary() mut expr := p.parse_primary()
for int(prec) < int(p.get_precedence(p.peek().type)) { for int(prec) < int(p.get_precedence(p.peek().type)) {
op_tok := p.next() op_tok := p.next()
if op_tok.type == .dot { expr = match op_tok.type {
expr = p.parse_member_access(expr) .dot {p.parse_member_access(expr)}
} else { .increment, .decrement {p.parse_unary_right(expr, op_tok.text)}
expr = p.parse_binary(expr, op_tok.text, p.get_precedence(op_tok.type)) else {p.parse_binary(expr, op_tok.text, p.get_precedence(op_tok.type))}
} }
} }
@@ -349,12 +357,7 @@ fn (mut p Parser) parse_ident(ident string) Expr {
} }
} }
if p.inside_struct {
return p.parse_class_member(ident)
}
return match p.peek().type { return match p.peek().type {
.increment, .decrement {UnaryExpr {ident: ident, op: p.next().text}}
.lparen {p.parse_call(ident)} .lparen {p.parse_call(ident)}
else {Variable{ident}} else {Variable{ident}}
} }
@@ -363,10 +366,19 @@ fn (mut p Parser) parse_ident(ident string) Expr {
fn (mut p Parser) parse_member_access(from Expr) MemberAccess { fn (mut p Parser) parse_member_access(from Expr) MemberAccess {
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")}
return MemberAccess {from: from, member: member_tok.text} 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(name string) ClassMember { 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(.identifier)
type_tok := p.peek() type_tok := p.peek()
@@ -376,12 +388,12 @@ fn (mut p Parser) parse_class_member(name string) ClassMember {
p.expect_ident_is_class(type_tok.text) p.expect_ident_is_class(type_tok.text)
type_tok.text type_tok.text
} }
else{parse_error("Expected type after class 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.next()
p.expect(.semicolon) p.expect(.semicolon)
return ClassMember{name: name, type: type_name} return ClassMember{name: member_name, type: type_name is_immutable: is_immut}
} }
fn (mut p Parser) parse_class_inst(name string) ClassInstantiation { fn (mut p Parser) parse_class_inst(name string) ClassInstantiation {
@@ -390,7 +402,7 @@ fn (mut p Parser) parse_class_inst(name string) ClassInstantiation {
if p.peek().type != .rbracket { if p.peek().type != .rbracket {
for { for {
member_values << p.parse_expr(.lowest) member_values << p.parse_expr(.base)
if p.peek().type == .comma { if p.peek().type == .comma {
p.next() p.next()
} else { } else {
@@ -408,7 +420,7 @@ fn (mut p Parser) parse_call(name string) FnCall {
if p.peek().type != .rparen { if p.peek().type != .rparen {
for { for {
args << p.parse_expr(.lowest) args << p.parse_expr(.base)
if p.peek().type == .comma { if p.peek().type == .comma {
p.next() p.next()
} else { } else {
@@ -425,7 +437,7 @@ fn (mut p Parser) parse_print() PrintExpr {
mut exprs := []Expr{} mut exprs := []Expr{}
mut types := []string{} mut types := []string{}
for p.peek().type != .rparen { for p.peek().type != .rparen {
expr := p.parse_expr(.lowest) expr := p.parse_expr(.base)
exprs << expr exprs << expr
types << p.get_expr_type(expr) types << p.get_expr_type(expr)
if p.peek().type == .comma { if p.peek().type == .comma {
@@ -438,7 +450,29 @@ fn (mut p Parser) parse_print() PrintExpr {
return PrintExpr{exprs: exprs, types: types} 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 { 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}")
}
}
right := p.parse_expr(prec) right := p.parse_expr(prec)
binary_expr := BinaryExpr{left, op, right} binary_expr := BinaryExpr{left, op, right}
@@ -453,7 +487,7 @@ fn (mut p Parser) parse_type(type string) Expr {
if p.peek().type == .lparen { if p.peek().type == .lparen {
p.next() p.next()
expr := p.parse_expr(.lowest) expr := p.parse_expr(.base)
p.expect(.rparen) p.expect(.rparen)
return TypeCast { return TypeCast {
@@ -466,7 +500,7 @@ fn (mut p Parser) parse_type(type string) Expr {
} }
fn (mut p Parser) parse_paren() ParenExpr { fn (mut p Parser) parse_paren() ParenExpr {
expr := p.parse_expr(.lowest) expr := p.parse_expr(.base)
p.expect(.rparen) p.expect(.rparen)
return ParenExpr{expr: expr} return ParenExpr{expr: expr}
} }
@@ -483,6 +517,20 @@ fn (mut p Parser) is_ident_class(ident string) bool {
return p.symbols.lookup_class(ident) != none 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 { fn (mut p Parser) get_expr_type(expr Expr) string {
return match expr { return match expr {
ParenExpr {p.get_expr_type(expr.expr)} ParenExpr {p.get_expr_type(expr.expr)}
@@ -492,6 +540,7 @@ fn (mut p Parser) get_expr_type(expr Expr) string {
StringLiteral {'string'} StringLiteral {'string'}
VoidExpr {'void'} VoidExpr {'void'}
TypeExpr {expr.name} TypeExpr {expr.name}
UnaryExpr {p.get_expr_type(expr.expr)}
BinaryExpr { BinaryExpr {
p.check_binary_expr_types(expr) p.check_binary_expr_types(expr)
left_t := p.get_expr_type(expr.left) left_t := p.get_expr_type(expr.left)
@@ -513,13 +562,7 @@ fn (mut p Parser) get_expr_type(expr Expr) string {
fninfo.type fninfo.type
} }
MemberAccess { MemberAccess {
mut type_from := p.get_expr_type(expr.from) expr.member_type
classinfo := p.symbols.lookup_class(type_from)or{parse_error("Non-existant class ${type_from}")}
if expr.member in classinfo.members_info {
classinfo.members_info[expr.member].type
} else {
parse_error("Undefined class member ${expr.member}")
}
} }
ClassInstantiation {expr.name} ClassInstantiation {expr.name}
else {"Tried getting type of unexpected Expr"} else {"Tried getting type of unexpected Expr"}
@@ -529,7 +572,7 @@ fn (mut p Parser) get_expr_type(expr Expr) string {
fn (mut p Parser) is_op_valid_for_type(type string, op string) bool { fn (mut p Parser) is_op_valid_for_type(type string, op string) bool {
global := ['=', '==', '!='] global := ['=', '==', '!=']
mut legal_ops := match type { mut legal_ops := match type {
'int', 'real' {['+', '-', '*', '/', '<', '>', '<=', '>=', '++', '--', '+=', '-=', '*=', '/=']} 'int', 'real' {['+', '-', '*', '/', '<', '>', '<=', '>=', '++', '--', '+=', '-=', '*=', '/=', '++', '--']}
'bool' {['=']} 'bool' {['=']}
else {[]} else {[]}
} }
@@ -586,7 +629,7 @@ fn (mut p Parser) parse_var_decl(is_const bool) VarDecl {
} }
p.expect(.equals) p.expect(.equals)
val := p.parse_expr(.lowest) val := p.parse_expr(.base)
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")
@@ -596,7 +639,7 @@ fn (mut p Parser) parse_var_decl(is_const bool) VarDecl {
} }
p.expect(.semicolon) p.expect(.semicolon)
p.symbols.define_var(name_tok.text, type_tok.text) p.symbols.define_var(name_tok.text, type_tok.text, is_const)
return VarDecl { return VarDecl {
name: name_tok.text name: name_tok.text
@@ -640,7 +683,7 @@ fn (mut p Parser) parse_func_decl() FuncDecl {
} }
p.next() p.next()
params << Param{p_name, p_type} params << Param{p_name, p_type}
p.symbols.define_var(p_name, p_type) p.symbols.define_var(p_name, p_type, false)
if p.peek().type == .comma { if p.peek().type == .comma {
p.next() p.next()
@@ -695,8 +738,8 @@ fn (mut p Parser) parse_class() ClassDecl {
p.expect(.identifier) p.expect(.identifier)
p.expect(.lbracket) p.expect(.lbracket)
mut members := []ClassMember{} mut members := []ClassMember{}
for p.peek().type == .identifier { for p.peek().type != .rbracket {
members << p.parse_class_member(p.peek().text) members << p.parse_class_member()
} }
p.expect(.rbracket) p.expect(.rbracket)
p.symbols.define_class(name, members) p.symbols.define_class(name, members)
@@ -706,7 +749,7 @@ fn (mut p Parser) parse_class() ClassDecl {
fn (mut p Parser) parse_return_stmt() ReturnStmt { fn (mut p Parser) parse_return_stmt() ReturnStmt {
p.expect(.kw_return) p.expect(.kw_return)
expr := p.parse_expr(.lowest) expr := p.parse_expr(.base)
p.expect(.semicolon) p.expect(.semicolon)
@@ -717,7 +760,7 @@ fn (mut p Parser) parse_return_stmt() ReturnStmt {
fn (mut p Parser) parse_if() IfStmt { fn (mut p Parser) parse_if() IfStmt {
p.expect(.kw_if) p.expect(.kw_if)
cond := p.parse_expr(.lowest) cond := p.parse_expr(.base)
if p.get_expr_type(cond) != 'bool' { if p.get_expr_type(cond) != 'bool' {
parse_error('If condition must be of type bool') parse_error('If condition must be of type bool')
} }
@@ -735,7 +778,7 @@ fn (mut p Parser) parse_else() ElseStmt {
fn (mut p Parser) parse_elif() ElifStmt { fn (mut p Parser) parse_elif() ElifStmt {
p.expect(.kw_elif) p.expect(.kw_elif)
cond := p.parse_expr(.lowest) cond := p.parse_expr(.base)
if p.get_expr_type(cond) != 'bool' { if p.get_expr_type(cond) != 'bool' {
parse_error('If condition must be of type bool') parse_error('If condition must be of type bool')
} }
@@ -745,7 +788,7 @@ fn (mut p Parser) parse_elif() ElifStmt {
} }
fn (mut p Parser) parse_expr_stmt() ExprStmt { fn (mut p Parser) parse_expr_stmt() ExprStmt {
expr := p.parse_expr(.lowest) expr := p.parse_expr(.base)
p.expect(.semicolon) p.expect(.semicolon)
return ExprStmt { return ExprStmt {
@@ -793,6 +836,10 @@ fn (mut p Parser) parse_block(no_scope bool) Block {
} }
fn (mut p Parser) parse_program() ([]Stmt, SymbolTable) { fn (mut p Parser) parse_program() ([]Stmt, SymbolTable) {
p.symbols.define_class('string',[
ClassMember{'len', 'int', true}
])
p.symbols.variable_scopes << map[string]VarSymbolInfo{} p.symbols.variable_scopes << map[string]VarSymbolInfo{}
for p.peek().type != .eof { for p.peek().type != .eof {
p.statements << p.parse_statement() p.statements << p.parse_statement()