Implement functions
- Implement function declaration and call - Change the precalculated variable stack positions to contain the offset from the end instead of the absolute position. This is important for passing fun args on the stack - Add the ability to offset the stackframes. This is used to delete the stack where the fun args have been stored before the block executes - Implement exit type for blocks in interpreter. This is used to get the return values and propagate them where needed - Add recursive fibonacci examples
This commit is contained in:
parent
f0c2bd8dde
commit
aeedfb4ef2
9
examples/recursive_fib.nek
Normal file
9
examples/recursive_fib.nek
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
fun fib(n) {
|
||||||
|
if n <= 1 {
|
||||||
|
return n;
|
||||||
|
} else {
|
||||||
|
return fib(n-1) + fib(n-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print fib(30);
|
||||||
6
examples/recursive_fib.py
Normal file
6
examples/recursive_fib.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
def fib(n):
|
||||||
|
if n <= 1:
|
||||||
|
return n
|
||||||
|
return fib(n-1) + fib(n-2)
|
||||||
|
|
||||||
|
print(fib(30))
|
||||||
14
src/ast.rs
14
src/ast.rs
@ -1,3 +1,5 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::stringstore::{StringStore, Sid};
|
use crate::stringstore::{StringStore, Sid};
|
||||||
|
|
||||||
/// Types for binary operators
|
/// Types for binary operators
|
||||||
@ -86,6 +88,8 @@ pub enum Expression {
|
|||||||
/// Array access with name, stackpos and position
|
/// Array access with name, stackpos and position
|
||||||
ArrayAccess(Sid, usize, Box<Expression>),
|
ArrayAccess(Sid, usize, Box<Expression>),
|
||||||
|
|
||||||
|
FunCall(Sid, usize, Vec<Expression>),
|
||||||
|
|
||||||
/// Variable
|
/// Variable
|
||||||
Var(Sid, usize),
|
Var(Sid, usize),
|
||||||
/// Binary operation. Consists of type, left hand side and right hand side
|
/// Binary operation. Consists of type, left hand side and right hand side
|
||||||
@ -114,9 +118,19 @@ pub struct If {
|
|||||||
pub body_false: BlockScope,
|
pub body_false: BlockScope,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
|
pub struct FunDecl {
|
||||||
|
pub name: Sid,
|
||||||
|
pub fun_stackpos: usize,
|
||||||
|
pub argnames: Vec<Sid>,
|
||||||
|
pub body: Rc<BlockScope>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum Statement {
|
pub enum Statement {
|
||||||
|
Return(Expression),
|
||||||
Declaration(Sid, usize, Expression),
|
Declaration(Sid, usize, Expression),
|
||||||
|
FunDeclare(FunDecl),
|
||||||
Expr(Expression),
|
Expr(Expression),
|
||||||
Block(BlockScope),
|
Block(BlockScope),
|
||||||
Loop(Loop),
|
Loop(Loop),
|
||||||
|
|||||||
@ -41,6 +41,8 @@ impl SimpleAstOptimizer {
|
|||||||
}
|
}
|
||||||
Statement::Print(expr) => Self::optimize_expr(expr),
|
Statement::Print(expr) => Self::optimize_expr(expr),
|
||||||
Statement::Declaration(_, _, expr) => Self::optimize_expr(expr),
|
Statement::Declaration(_, _, expr) => Self::optimize_expr(expr),
|
||||||
|
Statement::FunDeclare(_) => (),
|
||||||
|
Statement::Return(expr) => Self::optimize_expr(expr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
use std::cell::RefCell;
|
use std::{cell::RefCell, rc::Rc};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Ast, BinOpType, BlockScope, Expression, If, Statement, UnOpType},
|
ast::{Ast, BinOpType, BlockScope, Expression, FunDecl, If, Statement, UnOpType},
|
||||||
astoptimizer::{AstOptimizer, SimpleAstOptimizer},
|
astoptimizer::{AstOptimizer, SimpleAstOptimizer},
|
||||||
lexer::lex,
|
lexer::lex,
|
||||||
parser::parse,
|
parser::parse,
|
||||||
@ -25,6 +25,8 @@ pub enum RuntimeError {
|
|||||||
ArrayOutOfBounds(usize, usize),
|
ArrayOutOfBounds(usize, usize),
|
||||||
#[error("Division by zero")]
|
#[error("Division by zero")]
|
||||||
DivideByZero,
|
DivideByZero,
|
||||||
|
#[error("Invalid number of arguments for function {0}. Expected {1}, got {2}")]
|
||||||
|
InvalidNumberOfArgs(String, usize, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
@ -32,6 +34,13 @@ pub enum Value {
|
|||||||
I64(i64),
|
I64(i64),
|
||||||
String(Sid),
|
String(Sid),
|
||||||
Array(RefCell<Vec<Value>>),
|
Array(RefCell<Vec<Value>>),
|
||||||
|
Void,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
|
pub enum BlockExit {
|
||||||
|
Normal,
|
||||||
|
Return(Value),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -47,6 +56,8 @@ pub struct Interpreter {
|
|||||||
// Variable table stores the runtime values of variables
|
// Variable table stores the runtime values of variables
|
||||||
vartable: Vec<Value>,
|
vartable: Vec<Value>,
|
||||||
|
|
||||||
|
funtable: Vec<FunDecl>,
|
||||||
|
|
||||||
stringstore: StringStore,
|
stringstore: StringStore,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,10 +74,11 @@ impl Interpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_var(&self, idx: usize) -> Option<Value> {
|
fn get_var(&self, idx: usize) -> Option<Value> {
|
||||||
self.vartable.get(idx).cloned()
|
self.vartable.get(self.vartable.len() - idx - 1).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_var_mut(&mut self, idx: usize) -> Option<&mut Value> {
|
fn get_var_mut(&mut self, idx: usize) -> Option<&mut Value> {
|
||||||
|
let idx = self.vartable.len() - idx - 1;
|
||||||
self.vartable.get_mut(idx)
|
self.vartable.get_mut(idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,14 +104,30 @@ impl Interpreter {
|
|||||||
|
|
||||||
self.stringstore = ast.stringstore;
|
self.stringstore = ast.stringstore;
|
||||||
|
|
||||||
self.run_block(&ast.main)
|
self.run_block(&ast.main)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_block(&mut self, prog: &BlockScope) -> Result<(), RuntimeError> {
|
pub fn run_block(&mut self, prog: &BlockScope) -> Result<BlockExit, RuntimeError> {
|
||||||
let framepointer = self.vartable.len();
|
self.run_block_fp_offset(prog, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_block_fp_offset(
|
||||||
|
&mut self,
|
||||||
|
prog: &BlockScope,
|
||||||
|
framepointer_offset: usize,
|
||||||
|
) -> Result<BlockExit, RuntimeError> {
|
||||||
|
let framepointer = self.vartable.len() - framepointer_offset;
|
||||||
|
|
||||||
for stmt in prog {
|
for stmt in prog {
|
||||||
match stmt {
|
match stmt {
|
||||||
|
Statement::Return(expr) => {
|
||||||
|
let val = self.resolve_expr(expr)?;
|
||||||
|
|
||||||
|
self.vartable.truncate(framepointer);
|
||||||
|
return Ok(BlockExit::Return(val));
|
||||||
|
}
|
||||||
|
|
||||||
Statement::Expr(expr) => {
|
Statement::Expr(expr) => {
|
||||||
self.resolve_expr(expr)?;
|
self.resolve_expr(expr)?;
|
||||||
}
|
}
|
||||||
@ -110,7 +138,13 @@ impl Interpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Statement::Block(block) => {
|
Statement::Block(block) => {
|
||||||
self.run_block(block)?;
|
match self.run_block(block)? {
|
||||||
|
BlockExit::Return(val) => {
|
||||||
|
self.vartable.truncate(framepointer);
|
||||||
|
return Ok(BlockExit::Return(val));
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Statement::Loop(looop) => {
|
Statement::Loop(looop) => {
|
||||||
@ -143,18 +177,29 @@ impl Interpreter {
|
|||||||
body_true,
|
body_true,
|
||||||
body_false,
|
body_false,
|
||||||
}) => {
|
}) => {
|
||||||
if matches!(self.resolve_expr(condition)?, Value::I64(0)) {
|
let exit = if matches!(self.resolve_expr(condition)?, Value::I64(0)) {
|
||||||
self.run_block(body_false)?;
|
self.run_block(body_false)?
|
||||||
} else {
|
} else {
|
||||||
self.run_block(body_true)?;
|
self.run_block(body_true)?
|
||||||
|
};
|
||||||
|
match exit {
|
||||||
|
BlockExit::Return(val) => {
|
||||||
|
self.vartable.truncate(framepointer);
|
||||||
|
return Ok(BlockExit::Return(val));
|
||||||
}
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Statement::FunDeclare(fundec) => {
|
||||||
|
self.funtable.push(fundec.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.vartable.truncate(framepointer);
|
self.vartable.truncate(framepointer);
|
||||||
|
|
||||||
Ok(())
|
Ok(BlockExit::Normal)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_expr(&mut self, expr: &Expression) -> Result<Value, RuntimeError> {
|
fn resolve_expr(&mut self, expr: &Expression) -> Result<Value, RuntimeError> {
|
||||||
@ -174,6 +219,29 @@ impl Interpreter {
|
|||||||
Expression::ArrayAccess(name, idx, arr_idx) => {
|
Expression::ArrayAccess(name, idx, arr_idx) => {
|
||||||
self.resolve_array_access(*name, *idx, arr_idx)?
|
self.resolve_array_access(*name, *idx, arr_idx)?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expression::FunCall(fun_name, fun_stackpos, args) => {
|
||||||
|
for arg in args {
|
||||||
|
let arg = self.resolve_expr(arg)?;
|
||||||
|
self.vartable.push(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function existance has been verified in the parser, so unwrap here shouldn't fail
|
||||||
|
let num_args = self.funtable.get(*fun_stackpos).unwrap().argnames.len();
|
||||||
|
|
||||||
|
if num_args != args.len() {
|
||||||
|
let fun_name = self.stringstore.lookup(*fun_name).cloned().unwrap_or("<unknown>".to_string());
|
||||||
|
return Err(RuntimeError::InvalidNumberOfArgs(fun_name, num_args, args.len()));
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.run_block_fp_offset(
|
||||||
|
&Rc::clone(&self.funtable.get(*fun_stackpos).unwrap().body),
|
||||||
|
num_args,
|
||||||
|
)? {
|
||||||
|
BlockExit::Normal => Value::Void,
|
||||||
|
BlockExit::Return(val) => val,
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(val)
|
Ok(val)
|
||||||
@ -310,8 +378,12 @@ impl Interpreter {
|
|||||||
BinOpType::Add => Value::I64(lhs + rhs),
|
BinOpType::Add => Value::I64(lhs + rhs),
|
||||||
BinOpType::Mul => Value::I64(lhs * rhs),
|
BinOpType::Mul => Value::I64(lhs * rhs),
|
||||||
BinOpType::Sub => Value::I64(lhs - rhs),
|
BinOpType::Sub => Value::I64(lhs - rhs),
|
||||||
BinOpType::Div => Value::I64(lhs.checked_div(rhs).ok_or(RuntimeError::DivideByZero)?),
|
BinOpType::Div => {
|
||||||
BinOpType::Mod => Value::I64(lhs.checked_rem(rhs).ok_or(RuntimeError::DivideByZero)?),
|
Value::I64(lhs.checked_div(rhs).ok_or(RuntimeError::DivideByZero)?)
|
||||||
|
}
|
||||||
|
BinOpType::Mod => {
|
||||||
|
Value::I64(lhs.checked_rem(rhs).ok_or(RuntimeError::DivideByZero)?)
|
||||||
|
}
|
||||||
BinOpType::BOr => Value::I64(lhs | rhs),
|
BinOpType::BOr => Value::I64(lhs | rhs),
|
||||||
BinOpType::BAnd => Value::I64(lhs & rhs),
|
BinOpType::BAnd => Value::I64(lhs & rhs),
|
||||||
BinOpType::BXor => Value::I64(lhs ^ rhs),
|
BinOpType::BXor => Value::I64(lhs ^ rhs),
|
||||||
@ -338,7 +410,8 @@ impl Interpreter {
|
|||||||
match val {
|
match val {
|
||||||
Value::I64(val) => format!("{}", val),
|
Value::I64(val) => format!("{}", val),
|
||||||
Value::Array(val) => format!("{:?}", val.borrow()),
|
Value::Array(val) => format!("{:?}", val.borrow()),
|
||||||
Value::String(text) => format!("{}", self.stringstore.lookup(*text).unwrap()),
|
Value::String(text) => format!("{}", self.stringstore.lookup(*text).unwrap_or(&"<invalid string>".to_string())),
|
||||||
|
Value::Void => format!("void"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,6 +70,7 @@ impl<'a> Lexer<'a> {
|
|||||||
('|', '|') => self.push_tok_consume(T![||]),
|
('|', '|') => self.push_tok_consume(T![||]),
|
||||||
|
|
||||||
// Single character tokens
|
// Single character tokens
|
||||||
|
(',', _) => self.push_tok(T![,]),
|
||||||
(';', _) => self.push_tok(T![;]),
|
(';', _) => self.push_tok(T![;]),
|
||||||
('+', _) => self.push_tok(T![+]),
|
('+', _) => self.push_tok(T![+]),
|
||||||
('-', _) => self.push_tok(T![-]),
|
('-', _) => self.push_tok(T![-]),
|
||||||
@ -199,6 +200,8 @@ impl<'a> Lexer<'a> {
|
|||||||
"print" => T![print],
|
"print" => T![print],
|
||||||
"if" => T![if],
|
"if" => T![if],
|
||||||
"else" => T![else],
|
"else" => T![else],
|
||||||
|
"fun" => T![fun],
|
||||||
|
"return" => T![return],
|
||||||
|
|
||||||
// If it doesn't match a keyword, it is a normal identifier
|
// If it doesn't match a keyword, it is a normal identifier
|
||||||
_ => T![ident(ident)],
|
_ => T![ident(ident)],
|
||||||
|
|||||||
133
src/parser.rs
133
src/parser.rs
@ -1,7 +1,7 @@
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Ast, BlockScope, Expression, If, Loop, Statement},
|
ast::{Ast, BlockScope, Expression, FunDecl, If, Loop, Statement},
|
||||||
stringstore::{Sid, StringStore},
|
stringstore::{Sid, StringStore},
|
||||||
token::Token,
|
token::Token,
|
||||||
util::{PutBackIter, PutBackableExt},
|
util::{PutBackIter, PutBackableExt},
|
||||||
@ -16,6 +16,10 @@ pub enum ParseErr {
|
|||||||
DeclarationOfNonVar,
|
DeclarationOfNonVar,
|
||||||
#[error("Use of undefined variable \"{0}\"")]
|
#[error("Use of undefined variable \"{0}\"")]
|
||||||
UseOfUndeclaredVar(String),
|
UseOfUndeclaredVar(String),
|
||||||
|
#[error("Use of undefined function \"{0}\"")]
|
||||||
|
UseOfUndeclaredFun(String),
|
||||||
|
#[error("Redeclation of function \"{0}\"")]
|
||||||
|
RedeclarationFun(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResPE<T> = Result<T, ParseErr>;
|
type ResPE<T> = Result<T, ParseErr>;
|
||||||
@ -39,6 +43,7 @@ struct Parser<T: Iterator<Item = Token>> {
|
|||||||
tokens: PutBackIter<T>,
|
tokens: PutBackIter<T>,
|
||||||
string_store: StringStore,
|
string_store: StringStore,
|
||||||
var_stack: Vec<Sid>,
|
var_stack: Vec<Sid>,
|
||||||
|
fun_stack: Vec<Sid>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Iterator<Item = Token>> Parser<T> {
|
impl<T: Iterator<Item = Token>> Parser<T> {
|
||||||
@ -47,10 +52,12 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
let tokens = tokens.into_iter().putbackable();
|
let tokens = tokens.into_iter().putbackable();
|
||||||
let string_store = StringStore::new();
|
let string_store = StringStore::new();
|
||||||
let var_stack = Vec::new();
|
let var_stack = Vec::new();
|
||||||
|
let fun_stack = Vec::new();
|
||||||
Self {
|
Self {
|
||||||
tokens,
|
tokens,
|
||||||
string_store,
|
string_store,
|
||||||
var_stack,
|
var_stack,
|
||||||
|
fun_stack,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,10 +69,14 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_scoped_block(&mut self) -> ResPE<BlockScope> {
|
||||||
|
self.parse_scoped_block_fp_offset(0)
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse tokens into an abstract syntax tree. This will continuously parse statements until
|
/// Parse tokens into an abstract syntax tree. This will continuously parse statements until
|
||||||
/// encountering end-of-file or a block end '}' .
|
/// encountering end-of-file or a block end '}' .
|
||||||
fn parse_scoped_block(&mut self) -> ResPE<BlockScope> {
|
fn parse_scoped_block_fp_offset(&mut self, framepoint_offset: usize) -> ResPE<BlockScope> {
|
||||||
let framepointer = self.var_stack.len();
|
let framepointer = self.var_stack.len() - framepoint_offset;
|
||||||
let mut prog = Vec::new();
|
let mut prog = Vec::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@ -109,8 +120,80 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
Statement::Print(expr)
|
Statement::Print(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
T![return] => {
|
||||||
|
self.next();
|
||||||
|
let stmt = Statement::Return(self.parse_expr()?);
|
||||||
|
|
||||||
|
// After a statement, there must be a semicolon
|
||||||
|
validate_next!(self, T![;], ";");
|
||||||
|
|
||||||
|
stmt
|
||||||
|
}
|
||||||
|
|
||||||
T![if] => Statement::If(self.parse_if()?),
|
T![if] => Statement::If(self.parse_if()?),
|
||||||
|
|
||||||
|
T![fun] => {
|
||||||
|
self.next();
|
||||||
|
|
||||||
|
let fun_name = match self.next() {
|
||||||
|
T![ident(fun_name)] => fun_name,
|
||||||
|
tok => return Err(ParseErr::UnexpectedToken(tok, "<ident>".to_string())),
|
||||||
|
};
|
||||||
|
|
||||||
|
let fun_name = self.string_store.intern_or_lookup(&fun_name);
|
||||||
|
|
||||||
|
if self.fun_stack.contains(&fun_name) {
|
||||||
|
return Err(ParseErr::RedeclarationFun(
|
||||||
|
self.string_store
|
||||||
|
.lookup(fun_name)
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or("<unknown>".to_string()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let fun_stackpos = self.fun_stack.len();
|
||||||
|
self.fun_stack.push(fun_name);
|
||||||
|
|
||||||
|
let mut arg_names = Vec::new();
|
||||||
|
|
||||||
|
validate_next!(self, T!['('], "(");
|
||||||
|
|
||||||
|
while matches!(self.peek(), T![ident(_)]) {
|
||||||
|
let var_name = match self.next() {
|
||||||
|
T![ident(var_name)] => var_name,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let var_name = self.string_store.intern_or_lookup(&var_name);
|
||||||
|
arg_names.push(var_name);
|
||||||
|
|
||||||
|
// Push the variable onto the varstack
|
||||||
|
self.var_stack.push(var_name);
|
||||||
|
|
||||||
|
// If there are more args skip the comma so that the loop will read the argname
|
||||||
|
if self.peek() == &T![,] {
|
||||||
|
self.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_next!(self, T![')'], ")");
|
||||||
|
|
||||||
|
validate_next!(self, T!['{'], "{");
|
||||||
|
|
||||||
|
// Create the scoped block with a stack offset. This will pop the args that are
|
||||||
|
// added to the stack while parsing args
|
||||||
|
let body = self.parse_scoped_block_fp_offset(arg_names.len())?;
|
||||||
|
|
||||||
|
validate_next!(self, T!['}'], "}");
|
||||||
|
|
||||||
|
Statement::FunDeclare(FunDecl {
|
||||||
|
name: fun_name,
|
||||||
|
fun_stackpos,
|
||||||
|
argnames: arg_names,
|
||||||
|
body: body.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
let first = self.next();
|
let first = self.next();
|
||||||
|
|
||||||
@ -118,11 +201,12 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
(T![ident(name)], T![<-]) => {
|
(T![ident(name)], T![<-]) => {
|
||||||
self.next();
|
self.next();
|
||||||
|
|
||||||
|
let rhs = self.parse_expr()?;
|
||||||
|
|
||||||
let sid = self.string_store.intern_or_lookup(&name);
|
let sid = self.string_store.intern_or_lookup(&name);
|
||||||
let sp = self.var_stack.len();
|
let sp = self.var_stack.len();
|
||||||
self.var_stack.push(sid);
|
self.var_stack.push(sid);
|
||||||
|
|
||||||
let rhs = self.parse_expr()?;
|
|
||||||
Statement::Declaration(sid, sp, rhs)
|
Statement::Declaration(sid, sp, rhs)
|
||||||
}
|
}
|
||||||
(first, _) => {
|
(first, _) => {
|
||||||
@ -272,6 +356,31 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
Expression::ArrayAccess(sid, stackpos, index.into())
|
Expression::ArrayAccess(sid, stackpos, index.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
T![ident(name)] if self.peek() == &T!['('] => {
|
||||||
|
// Skip the opening parenthesis
|
||||||
|
self.next();
|
||||||
|
|
||||||
|
let sid = self.string_store.intern_or_lookup(&name);
|
||||||
|
|
||||||
|
let mut args = Vec::new();
|
||||||
|
|
||||||
|
while !matches!(self.peek(), T![')']) {
|
||||||
|
let arg = self.parse_expr()?;
|
||||||
|
args.push(arg);
|
||||||
|
|
||||||
|
// If there are more args skip the comma so that the loop will read the argname
|
||||||
|
if self.peek() == &T![,] {
|
||||||
|
self.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_next!(self, T![')'], ")");
|
||||||
|
|
||||||
|
let fun_stackpos = self.get_fun_stackpos(sid)?;
|
||||||
|
|
||||||
|
Expression::FunCall(sid, fun_stackpos, args)
|
||||||
|
}
|
||||||
|
|
||||||
T![ident(name)] => {
|
T![ident(name)] => {
|
||||||
let sid = self.string_store.intern_or_lookup(&name);
|
let sid = self.string_store.intern_or_lookup(&name);
|
||||||
let stackpos = self.get_stackpos(sid)?;
|
let stackpos = self.get_stackpos(sid)?;
|
||||||
@ -303,7 +412,7 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.position(|it| *it == varid)
|
.position(|it| *it == varid)
|
||||||
.map(|it| self.var_stack.len() - it - 1)
|
.map(|it| it)
|
||||||
.ok_or(ParseErr::UseOfUndeclaredVar(
|
.ok_or(ParseErr::UseOfUndeclaredVar(
|
||||||
self.string_store
|
self.string_store
|
||||||
.lookup(varid)
|
.lookup(varid)
|
||||||
@ -312,6 +421,20 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_fun_stackpos(&self, varid: Sid) -> ResPE<usize> {
|
||||||
|
self.fun_stack
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.position(|it| *it == varid)
|
||||||
|
.map(|it| self.fun_stack.len() - it - 1)
|
||||||
|
.ok_or(ParseErr::UseOfUndeclaredFun(
|
||||||
|
self.string_store
|
||||||
|
.lookup(varid)
|
||||||
|
.map(String::from)
|
||||||
|
.unwrap_or("<unknown>".to_string()),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the next Token without removing it
|
/// Get the next Token without removing it
|
||||||
fn peek(&mut self) -> &Token {
|
fn peek(&mut self) -> &Token {
|
||||||
self.tokens.peek().unwrap_or(&T![EoF])
|
self.tokens.peek().unwrap_or(&T![EoF])
|
||||||
|
|||||||
24
src/token.rs
24
src/token.rs
@ -1,4 +1,7 @@
|
|||||||
use crate::{ast::{BinOpType, UnOpType}, T};
|
use crate::{
|
||||||
|
ast::{BinOpType, UnOpType},
|
||||||
|
T,
|
||||||
|
};
|
||||||
|
|
||||||
/// Language keywords
|
/// Language keywords
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
@ -11,6 +14,10 @@ pub enum Keyword {
|
|||||||
If,
|
If,
|
||||||
/// Else keyword ("else")
|
/// Else keyword ("else")
|
||||||
Else,
|
Else,
|
||||||
|
/// Function declaration keyword ("fun")
|
||||||
|
Fun,
|
||||||
|
/// Return keyword ("return")
|
||||||
|
Return,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Literal values
|
/// Literal values
|
||||||
@ -67,6 +74,9 @@ pub enum Token {
|
|||||||
/// Combined tokens consisting of multiple characters
|
/// Combined tokens consisting of multiple characters
|
||||||
Combo(Combo),
|
Combo(Combo),
|
||||||
|
|
||||||
|
/// Comma (",")
|
||||||
|
Comma,
|
||||||
|
|
||||||
/// Equal Sign ("=")
|
/// Equal Sign ("=")
|
||||||
Equal,
|
Equal,
|
||||||
|
|
||||||
@ -199,6 +209,14 @@ macro_rules! T {
|
|||||||
crate::token::Token::Keyword(crate::token::Keyword::Else)
|
crate::token::Token::Keyword(crate::token::Keyword::Else)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[fun] => {
|
||||||
|
crate::token::Token::Keyword(crate::token::Keyword::Fun)
|
||||||
|
};
|
||||||
|
|
||||||
|
[return] => {
|
||||||
|
crate::token::Token::Keyword(crate::token::Keyword::Return)
|
||||||
|
};
|
||||||
|
|
||||||
// Literals
|
// Literals
|
||||||
[i64($($val:tt)*)] => {
|
[i64($($val:tt)*)] => {
|
||||||
crate::token::Token::Literal(crate::token::Literal::I64($($val)*))
|
crate::token::Token::Literal(crate::token::Literal::I64($($val)*))
|
||||||
@ -251,6 +269,10 @@ macro_rules! T {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Normal Tokens
|
// Normal Tokens
|
||||||
|
[,] => {
|
||||||
|
crate::token::Token::Comma
|
||||||
|
};
|
||||||
|
|
||||||
[=] => {
|
[=] => {
|
||||||
crate::token::Token::Equal
|
crate::token::Token::Equal
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user