diff --git a/euler/src/main.rs b/euler/src/main.rs index c85362e..ebbcc7a 100644 --- a/euler/src/main.rs +++ b/euler/src/main.rs @@ -1,4 +1,4 @@ -use std::{env, fs}; +use std::{env, fs, path::PathBuf}; use clap::{self, Args, Parser, Subcommand, ValueEnum}; use mathic::{ @@ -21,8 +21,15 @@ struct MathiCLI { #[derive(Debug, Subcommand)] enum Command { - New { project_name: String }, + New { + project_name: String, + }, Run(CompilerOptionsArgs), + Path { + path: PathBuf, + #[clap(flatten)] + opts: CompilerOptionsArgs, + }, } #[derive(Debug, Clone, Args)] @@ -74,6 +81,7 @@ fn main() -> Result<(), EulerError> { Command::Run(compiler_opts) => { compile_project(compiler_opts.into())?; } + Command::Path { path, opts } => compile_path(path, opts.into())?, }; Ok(()) @@ -133,3 +141,28 @@ fn compile_project(compiler_opts: CompilerOpts) -> Result<(), EulerError> { Ok(()) } + +fn compile_path(path: PathBuf, compiler_opts: CompilerOpts) -> Result<(), EulerError> { + let compiler = MathicCompiler::new()?; + + let module = match compiler.compile_path(&path, compiler_opts) { + Ok(modules) => modules, + Err(MathicError::CompilationFailed) => { + compiler.diagnostics().print_all()?; + std::process::exit(1); + } + Err(e) => { + return Err(EulerError::from(e)); + } + }; + + let executor = MathicJITExecutor::new(vec![module], compiler_opts)?; + + tracing::debug!("Executor Created"); + let result = executor.call_function("main::main"); + + tracing::debug!("Execution Done"); + println!("RESULT: {:?}", result); + + Ok(()) +} diff --git a/examples/structs/struct_methods.mth b/examples/structs/struct_methods.mth new file mode 100644 index 0000000..4bb05af --- /dev/null +++ b/examples/structs/struct_methods.mth @@ -0,0 +1,34 @@ +struct Point { + x: u32, + y: u32 +} + +expand Point { + df new(x: u32, y: u32) Point { + return Point { + x: x, + y: y + }; + } + + df add(self, rhs: Point) Point { + return Point { + x: self.x + rhs.x, + y: self.y + rhs.y + }; + } +} + +df main() { + let point_1: Point = Point { + x: 5, + y: 5 + }; + + let point_2: Point = Point { + x: 10, + y: 10 + }; + + return point_1.add(point_2); +} diff --git a/grammar.ebnf b/grammar.ebnf index 2d69e80..491b6d7 100644 --- a/grammar.ebnf +++ b/grammar.ebnf @@ -20,10 +20,12 @@ program = top_decl { top_decl } ; top_decl = func_decl | struct_decl | imports_decls + | expand_decl ; imports_decls = 'imp' import_path ';' ; func_decl = 'df' IDENT '(' [ param_list ] ')' [ type ] block ; struct_decl = 'struct' IDENT '{' [ struct_fields ] '}' ; +expand_decl = 'expand' path '{' [ func_decl ] '}' ; (* ================================================================ *) @@ -32,6 +34,7 @@ struct_decl = 'struct' IDENT '{' [ struct_fields ] '}' ; declaration = func_decl | struct_decl + | expand_decl | var_decl | sym_decl ; @@ -90,7 +93,8 @@ factor = unary { ( '*' | '/' ) unary } ; unary = ( '!' | '-' ) unary | call ; -call = primary { '(' [ arg_list ] ')' | '.' IDENT +call = primary { '(' [ arg_list ] ')' + | '.' IDENT [ (' [ arg_list ] ')' ] | '[' bracket_args ']' } ; bracket_args = substitution ; struct_init = '{' IDENT ':' expr { ',' IDENT ':' expr } '}' ; @@ -102,7 +106,8 @@ primary = 'true' | 'false' | path | INT | FLOAT | STRING | '(' expr ')' ; (* Utilities *) (* ================================================================ *) -param_list = ( IDENT ':' type ) { ',' ( IDENT ':' type ) } ; +param_list = [ 'self' ',' ] param_list + | ( IDENT ':' type ) { ',' ( IDENT ':' type ) } ; struct_fields = [ 'pub' ] IDENT ':' type { ',' [ 'pub' ] IDENT ':' type } ; arg_list = expr { ',' expr } ; import_path = IDENT diff --git a/src/compiler.rs b/src/compiler.rs index 8128e8f..9652132 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -286,7 +286,8 @@ impl MathicCompiler { } } }; - + dbg!(ast); + todo!(); // AST lowering and semantic checks. let ir = match lowering::lower_program(&ast) { Ok(ir) => ir, diff --git a/src/diagnostics/parse.rs b/src/diagnostics/parse.rs index c1637b7..490f3c1 100644 --- a/src/diagnostics/parse.rs +++ b/src/diagnostics/parse.rs @@ -45,6 +45,12 @@ pub enum ExpectedToken { Custom(String), } +impl From for ExpectedToken { + fn from(value: Token) -> Self { + Self::Token(value) + } +} + impl Display for ExpectedToken { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { diff --git a/src/lowering.rs b/src/lowering.rs index 31955d1..db50f9a 100644 --- a/src/lowering.rs +++ b/src/lowering.rs @@ -41,7 +41,8 @@ pub fn lower_program(program: &IrModule) -> Result { // of a not yet declared function. for item in program.items.iter() { match item { - TopLevelItem::Func(f) => ir_builder.decl_table.add_func_decl(f.clone(), None)?, + TopLevelItem::ExpandBlock(_) => todo!(), + TopLevelItem::Func(f) => ir_builder.decl_table.add_func_decl(f.clone(), None, None)?, TopLevelItem::Import(imp) => lower_import(&mut ir_builder, imp)?, TopLevelItem::Struct(s) => ir_builder.decl_table.add_struct_decl(s.clone(), None)?, } @@ -110,7 +111,7 @@ fn lower_import(ir_builder: &mut IrBuilder, import_path: &Path) -> Result<(), Lo TopLevelItem::Func(func) => { ir_builder .decl_table - .add_func_decl(func.clone(), Some(module_idx))?; + .add_func_decl(func.clone(), None, Some(module_idx))?; utils::add_extern_function( ir_builder, &module.module_name, @@ -168,7 +169,9 @@ fn lower_top_level_function( // of a not yet declared function. for stmt in body.iter() { if let StmtKind::Decl(DeclStmt::Func(f)) = &stmt.kind { - func_builder.decl_table.add_func_decl(f.clone(), None)?; + func_builder + .decl_table + .add_func_decl(f.clone(), None, None)?; } } @@ -178,7 +181,7 @@ fn lower_top_level_function( let func = func_builder.build(); - ir_builder.add_function(func); + ir_builder.add_function(func, None); Ok(()) } @@ -277,5 +280,6 @@ pub fn lower_top_level_ast_type( } } } + AstType::SelfType => todo!(), }) } diff --git a/src/lowering/ast_lowering/declaration.rs b/src/lowering/ast_lowering/declaration.rs index 844b7d2..1db6194 100644 --- a/src/lowering/ast_lowering/declaration.rs +++ b/src/lowering/ast_lowering/declaration.rs @@ -155,7 +155,7 @@ pub fn lower_inner_function( for stmt in body.iter() { match &stmt.kind { StmtKind::Decl(DeclStmt::Func(f)) => { - inner_func.decl_table.add_func_decl(f.clone(), None)? + inner_func.decl_table.add_func_decl(f.clone(), None, None)? } StmtKind::Decl(DeclStmt::Struct(s)) => { inner_func.decl_table.add_struct_decl(s.clone(), None)? @@ -170,7 +170,7 @@ pub fn lower_inner_function( let inner_func = inner_func.build(); - func.sym_table.add_function(inner_func); + func.sym_table.add_function(inner_func, None); Ok(()) } diff --git a/src/lowering/ast_lowering/expression.rs b/src/lowering/ast_lowering/expression.rs index dd1a296..aa592c4 100644 --- a/src/lowering/ast_lowering/expression.rs +++ b/src/lowering/ast_lowering/expression.rs @@ -54,6 +54,7 @@ pub fn lower_expr( field_name, rhs, } => lower_struct_set(func, lhs, field_name, rhs, expr.span)?, + ExprStmtKind::MethodCall { callee: _, args: _ } => unimplemented!(), }; Ok(( @@ -821,6 +822,7 @@ fn lower_expression_type( None => func.get_or_insert_global_type_idx(MathicType::Void), } } + ExprStmtKind::MethodCall { callee: _, args: _ } => unimplemented!(), ExprStmtKind::Group(expr_stmt) => lower_expression_type(func, &expr_stmt.kind, None, span)?, ExprStmtKind::Index { .. } => todo!(), ExprStmtKind::Logical { .. } => func.get_or_insert_global_type_idx(MathicType::Bool), diff --git a/src/lowering/ast_lowering/statement.rs b/src/lowering/ast_lowering/statement.rs index 3204778..820dc46 100644 --- a/src/lowering/ast_lowering/statement.rs +++ b/src/lowering/ast_lowering/statement.rs @@ -79,6 +79,7 @@ fn lower_declaration( DeclStmt::Struct(struct_decl) => { let _ = lower_inner_struct(func, struct_decl)?; } + DeclStmt::ExpandDecl(expand_block_) => unimplemented!(), DeclStmt::Sym(sym_decl) => lower_sym_decl(func, sym_decl, *span)?, DeclStmt::Func(func_decl) => lower_inner_function(func, func_decl, *span)?, } diff --git a/src/lowering/ir.rs b/src/lowering/ir.rs index 60a8986..7e9bc0c 100644 --- a/src/lowering/ir.rs +++ b/src/lowering/ir.rs @@ -4,7 +4,7 @@ use crate::{ diagnostics::LoweringError, lowering::ir::{ adts::Adt, - function::Function, + function::{FuncId, Function}, symbols::{DeclTable, SymbolTableBuilder, TypeIndex}, types::MathicType, }, @@ -67,8 +67,12 @@ impl IrBuilder { } } - pub fn add_function(&mut self, func: Function) { - self.sym_table.functions.insert(func.name.clone(), func); + pub fn add_function(&mut self, func: Function, method_of: Option) { + let func_id = FuncId { + name: func.name.clone(), + method_of, + }; + self.sym_table.functions.insert(func_id, func); } pub fn get_type(&self, idx: TypeIndex, span: Span) -> Result { diff --git a/src/lowering/ir/function.rs b/src/lowering/ir/function.rs index 3ae5ae5..6eb077b 100644 --- a/src/lowering/ir/function.rs +++ b/src/lowering/ir/function.rs @@ -25,7 +25,6 @@ pub enum LocalKind { /// MATHIR's representation of local variables. #[derive(Debug, Clone)] -#[allow(dead_code)] pub struct Local { pub local_idx: usize, pub kind: LocalKind, @@ -35,9 +34,14 @@ pub struct Local { pub symbols: HashSet, } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct FuncId { + pub name: String, + pub method_of: Option, +} + /// MATHIR's representation of a function. #[derive(Debug, Clone)] -#[allow(dead_code)] pub struct Function { pub name: String, sym_table: SymbolTable, @@ -147,9 +151,14 @@ impl<'ir> FunctionBuilder<'ir> { name: &str, span: Span, ) -> Result<(FuncDecl, Option), LoweringError> { - match self.decl_table.get_function_decl(name).cloned() { + match self.decl_table.get_function_decl(name, None).cloned() { Some(f) => Ok(f), - None => match self.ir_builder.decl_table.get_function_decl(name).cloned() { + None => match self + .ir_builder + .decl_table + .get_function_decl(name, None) + .cloned() + { Some(f) => Ok(f), None => Err(LoweringError::UndeclaredFunction { name: name.to_string(), diff --git a/src/lowering/ir/symbols.rs b/src/lowering/ir/symbols.rs index 153b100..6eefc0f 100644 --- a/src/lowering/ir/symbols.rs +++ b/src/lowering/ir/symbols.rs @@ -7,7 +7,7 @@ use crate::{ diagnostics::LoweringError, lowering::ir::{ adts::Adt, - function::{Function, Local, LocalKind}, + function::{FuncId, Function, Local, LocalKind}, types::MathicType, }, parser::{ @@ -24,11 +24,10 @@ use crate::{ /// Use to store function, struct and enum declarations to allow for /// forward referencing. #[derive(Debug, Clone, Default)] -#[allow(dead_code)] pub struct DeclTable { pub name_to_module: HashMap, pub modules: Vec>, - functions: HashMap)>, + functions: HashMap)>, structs: HashMap)>, } @@ -49,18 +48,22 @@ impl DeclTable { pub fn add_func_decl( &mut self, func: FuncDecl, + method_of: Option, module_idx: Option, ) -> Result<(), LoweringError> { - let name = func.name.clone(); + let func_id = FuncId { + name: func.name.clone(), + method_of, + }; - if self.functions.contains_key(&name) { + if self.functions.contains_key(&func_id) { return Err(LoweringError::DuplicateDeclaration { - name, + name: func_id.name, span: func.span, }); } - self.functions.insert(name, (func, module_idx)); + self.functions.insert(func_id, (func, module_idx)); Ok(()) } @@ -83,8 +86,16 @@ impl DeclTable { Ok(()) } - pub fn get_function_decl(&self, name: &str) -> Option<&(FuncDecl, Option)> { - self.functions.get(name) + pub fn get_function_decl( + &self, + name: &str, + method_of: Option, + ) -> Option<&(FuncDecl, Option)> { + let func_id = FuncId { + name: name.to_owned(), + method_of, + }; + self.functions.get(&func_id) } pub fn get_struct_decl(&self, name: &str) -> Option<&(StructDecl, Option)> { @@ -100,7 +111,7 @@ impl DeclTable { } } -#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct TypeIndex { pub idx: usize, pub is_local: bool, @@ -147,7 +158,7 @@ pub struct SymbolTableBuilder { types: TypeTable, pub locals: Vec, pub local_indexes: HashMap, - pub functions: HashMap, + pub functions: HashMap, pub user_def_types: HashMap, pub adts: Vec, } @@ -180,8 +191,12 @@ impl SymbolTableBuilder { }) } - pub fn add_function(&mut self, func: Function) { - self.functions.insert(func.name.clone(), func); + pub fn add_function(&mut self, func: Function, method_of: Option) { + let func_id = FuncId { + name: func.name.clone(), + method_of, + }; + self.functions.insert(func_id, func); } pub fn get_user_def_type(&self, name: &str) -> Option { diff --git a/src/lowering/ir/types.rs b/src/lowering/ir/types.rs index 8032108..52179d6 100644 --- a/src/lowering/ir/types.rs +++ b/src/lowering/ir/types.rs @@ -142,6 +142,7 @@ pub fn lower_inner_ast_type( } } }, + AstType::SelfType => todo!(), }) } diff --git a/src/lowering/utils.rs b/src/lowering/utils.rs index 74a08c0..d981b7e 100644 --- a/src/lowering/utils.rs +++ b/src/lowering/utils.rs @@ -1,7 +1,12 @@ use crate::{ diagnostics::LoweringError, lowering::{ - ir::{IrBuilder, function::FunctionBuilder, symbols::TypeIndex, types::MathicType}, + ir::{ + IrBuilder, + function::{FuncId, FunctionBuilder}, + symbols::TypeIndex, + types::MathicType, + }, lower_top_level_ast_type, lower_top_level_struct, }, parser::{ @@ -32,7 +37,7 @@ pub fn add_extern_function( )? .build(); - ir_builder.add_function(extern_func); + ir_builder.add_function(extern_func, None); Ok(()) } @@ -77,10 +82,13 @@ pub fn resolve_external_func( // The function may already be declared by a path call (mangled // name) or by an import (non-mangled name). - let declared_by_path = ir_builder.sym_table.functions.contains_key(&mangled_name); + let declared_by_path = ir_builder.sym_table.functions.contains_key(&FuncId { + name: mangled_name, + method_of: None, + }); let declared_by_import = ir_builder .decl_table - .get_function_decl(&func.name) + .get_function_decl(&func.name, None) .is_some_and(|(_, module)| *module == Some(module_idx)); if !(declared_by_path || declared_by_import) { diff --git a/src/parser.rs b/src/parser.rs index db8827c..19b2774 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -77,6 +77,7 @@ impl<'a> MathicParser<'a> { { match token { Token::Df => items.push(TopLevelItem::Func(self.parse_func()?)), + Token::Expand => items.push(TopLevelItem::ExpandBlock(self.parse_expand_block()?)), Token::Import => items.push(TopLevelItem::Import(self.parse_import()?)), Token::Struct => items.push(TopLevelItem::Struct(self.parse_struct()?)), _ => { @@ -141,7 +142,7 @@ impl<'a> MathicParser<'a> { self.current_span.get() } - /// Consumes the next token. + /// Consumes the expected token. /// /// Returns a parser error if the token does not match the expected one. fn consume_token(&self, expected: Token) -> ParserResult> { @@ -149,9 +150,9 @@ impl<'a> MathicParser<'a> { if res.token == expected { Ok(res) } else { - Err(ParseError::Syntax(SyntaxError::MissingToken { - expected, - span: res.span, + Err(ParseError::Syntax(SyntaxError::UnexpectedToken { + found: res.into(), + expected: expected.into(), })) } } else { diff --git a/src/parser/ast/declaration.rs b/src/parser/ast/declaration.rs index 9204e39..74525f8 100644 --- a/src/parser/ast/declaration.rs +++ b/src/parser/ast/declaration.rs @@ -5,6 +5,14 @@ pub enum TopLevelItem { Func(FuncDecl), Import(Path), Struct(StructDecl), + ExpandBlock(ExpandDecl), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ExpandDecl { + pub adt_name: Path, + pub methods: Vec, + pub span: Span, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -12,6 +20,7 @@ pub enum DeclStmt { Var(VarDecl), Sym(SymDecl), Struct(StructDecl), + ExpandDecl(ExpandDecl), Func(FuncDecl), } @@ -55,8 +64,8 @@ pub struct FuncDecl { #[derive(Debug, Clone, PartialEq, Eq)] pub struct Param { pub name: String, - pub span: Span, pub ty: AstType, + pub span: Span, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -69,6 +78,7 @@ pub struct Path { #[derive(Debug, Clone, PartialEq, Eq)] pub enum AstType { + SelfType, Type { ty: String, inner: Option>, @@ -82,6 +92,7 @@ impl TopLevelItem { TopLevelItem::Func(item) => item.name.clone(), TopLevelItem::Struct(item) => item.name.clone(), TopLevelItem::Import(item) => item.join("_"), + _ => panic!(), } } } diff --git a/src/parser/ast/expression.rs b/src/parser/ast/expression.rs index 617dc28..dc4fa59 100644 --- a/src/parser/ast/expression.rs +++ b/src/parser/ast/expression.rs @@ -21,6 +21,10 @@ pub enum ExprStmtKind { expr: Box, fields: HashMap, }, + MethodCall { + callee: Box, + args: Vec, + }, Call { callee: Box, args: Vec, diff --git a/src/parser/parsing/declaration.rs b/src/parser/parsing/declaration.rs index 794eb52..99c5eb4 100644 --- a/src/parser/parsing/declaration.rs +++ b/src/parser/parsing/declaration.rs @@ -1,7 +1,9 @@ use crate::parser::{ MathicParser, ParserResult, Span, ast::{ - declaration::{AstType, FuncDecl, Param, Path, StructDecl, StructField, SymDecl, VarDecl}, + declaration::{ + AstType, ExpandDecl, FuncDecl, Param, Path, StructDecl, StructField, SymDecl, VarDecl, + }, statement::BlockStmt, }, token::Token, @@ -27,6 +29,31 @@ impl<'a> MathicParser<'a> { Ok(ty) } + pub fn parse_expand_block(&self) -> ParserResult { + let start_span = self.next()?.span; + let mut methods = Vec::new(); + + let adt_name = self.parse_path()?; + + self.consume_token(Token::LBrace)?; + + while let Some(tk) = self.peek()? + && tk.token == Token::Df + { + methods.push(self.parse_func()?); + } + + self.consume_token(Token::RBrace)?; + + let span = Span::from_merged_spans(start_span, self.current_span()); + + Ok(ExpandDecl { + adt_name, + methods, + span, + }) + } + pub fn parse_func(&self) -> ParserResult { let start_span = self.next()?.span; // Consume Df. @@ -131,15 +158,29 @@ impl<'a> MathicParser<'a> { } fn parse_params(&self) -> ParserResult> { - let identifier = self.consume_token(Token::Ident)?; - self.consume_token(Token::Colon)?; - let ty = self.parse_type()?; + let first_param = { + let lookahead = self.consume_token(Token::Ident)?; + + match lookahead.lexeme { + "self" => Param { + name: lookahead.lexeme.to_string(), + ty: AstType::SelfType, + span: lookahead.span, + }, + _ => { + self.consume_token(Token::Colon)?; + let ty = self.parse_type()?; + + Param { + name: lookahead.lexeme.to_string(), + ty, + span: lookahead.span, + } + } + } + }; - let mut params = vec![Param { - name: identifier.lexeme.to_string(), - span: identifier.span, - ty, - }]; + let mut params = vec![first_param]; while self.match_token(Token::Comma)?.is_some() { let identifier = self.consume_token(Token::Ident)?; @@ -148,8 +189,8 @@ impl<'a> MathicParser<'a> { params.push(Param { name: identifier.lexeme.to_string(), - span: identifier.span, ty, + span: identifier.span, }); } diff --git a/src/parser/parsing/expression.rs b/src/parser/parsing/expression.rs index a3a7252..8f436d8 100644 --- a/src/parser/parsing/expression.rs +++ b/src/parser/parsing/expression.rs @@ -289,6 +289,7 @@ impl<'a> MathicParser<'a> { fn parse_call(&self) -> ParserResult { let lookahead = self.peek_not_none()?; let mut expr = self.parse_primary_expr()?; + let old_span = expr.span; while matches!(lookahead.token, Token::Ident) && self.check_next_any(&[Token::LParen, Token::Dot, Token::LSquareBracket])? @@ -301,13 +302,27 @@ impl<'a> MathicParser<'a> { } Token::Dot => { let field_name = self.consume_token(Token::Ident)?.lexeme.to_string(); - - expr = ExprStmt { - kind: ExprStmtKind::StructGet { - expr: Box::new(expr), - field_name, - }, - span: self.current_span(), + let old_span = expr.span; + + expr = if self.match_token(Token::LParen)?.is_some() { + let args = self.parse_call_args()?; + self.consume_token(Token::RParen)?; + + ExprStmt { + kind: ExprStmtKind::MethodCall { + callee: Box::new(expr), + args, + }, + span: Span::from_merged_spans(old_span, self.current_span()), + } + } else { + ExprStmt { + kind: ExprStmtKind::StructGet { + expr: Box::new(expr), + field_name, + }, + span: Span::from_merged_spans(old_span, self.current_span()), + } }; } Token::LSquareBracket => { @@ -320,7 +335,7 @@ impl<'a> MathicParser<'a> { callee: Box::new(expr), args, }, - span: self.current_span(), + span: Span::from_merged_spans(old_span, self.current_span()), }; } _ => {} diff --git a/src/parser/parsing/statement.rs b/src/parser/parsing/statement.rs index fde75db..3cb3c63 100644 --- a/src/parser/parsing/statement.rs +++ b/src/parser/parsing/statement.rs @@ -46,6 +46,12 @@ impl<'a> MathicParser<'a> { let span = Span::from_merged_spans(start_span, self.current_span()); (StmtKind::Decl(DeclStmt::Struct(struct_decl)), span) } + Token::Expand => { + let expand_block = self.parse_expand_block()?; + let span = Span::from_merged_spans(start_span, self.current_span()); + + (StmtKind::Decl(DeclStmt::ExpandDecl(expand_block)), span) + } Token::Sym => { let sym = self.parse_sym_decl()?; let span = Span::from_merged_spans(start_span, self.current_span()); diff --git a/src/parser/token.rs b/src/parser/token.rs index 362880d..1ab8ed1 100644 --- a/src/parser/token.rs +++ b/src/parser/token.rs @@ -63,6 +63,8 @@ pub enum Token { If, #[token("else")] Else, + #[token("expand")] + Expand, #[token("while")] While, #[token("for")]