Compare commits
39 Commits
vec-based-
...
feature-fu
| Author | SHA1 | Date | |
|---|---|---|---|
| e0c72003f9 | |||
| 638610d310 | |||
| 85339db25e | |||
| 88c5be6439 | |||
| 75e326e343 | |||
| 7490494bbf | |||
| 2a04a28f97 | |||
| b6d615b507 | |||
| 2946e67349 | |||
| 3c0e5f6b4d | |||
| dd6129bb00 | |||
| 308bc5b34e | |||
| 771a858da3 | |||
| eebe4a4c1c | |||
| 28d7f3ec03 | |||
| 64bd2341b8 | |||
| 9a7de0a1c6 | |||
| 99e462f4b5 | |||
| 4357a1eb55 | |||
| c4f5b89456 | |||
| c49a5ec0e2 | |||
| 49ada446f8 | |||
| 2ea2aa5203 | |||
| 14e8a0b507 | |||
| 07636d420c | |||
| 4ad16a71f4 | |||
| 8488e48364 | |||
| e80cae11c9 | |||
| 88ceacd500 | |||
| 1079eb1671 | |||
| 8a1debabe9 | |||
| fabe3ef2ad | |||
| 3535fec208 | |||
| 26c36ed0ae | |||
| 807482583a | |||
| 7b86fecc6f | |||
| 6b91264f84 | |||
| d9246c7ea1 | |||
| 1c4943828f |
@@ -1,28 +0,0 @@
|
|||||||
// 2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder.
|
|
||||||
// What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?
|
|
||||||
//
|
|
||||||
// Correct Answer: 232_792_560
|
|
||||||
|
|
||||||
num <- 20;
|
|
||||||
should_continue <- 1;
|
|
||||||
i <- 2;
|
|
||||||
|
|
||||||
loop should_continue {
|
|
||||||
should_continue = 0;
|
|
||||||
|
|
||||||
i = 20;
|
|
||||||
loop i >= 2; i = i - 1 {
|
|
||||||
if num % i != 0 {
|
|
||||||
should_continue = 1;
|
|
||||||
|
|
||||||
// break
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if should_continue == 1 {
|
|
||||||
num = num + 20;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print num;
|
|
||||||
30
src/ast.rs
30
src/ast.rs
@@ -82,6 +82,8 @@ pub enum Expression {
|
|||||||
I64(i64),
|
I64(i64),
|
||||||
/// String literal
|
/// String literal
|
||||||
String(Rc<String>),
|
String(Rc<String>),
|
||||||
|
|
||||||
|
FunCall(String, Vec<Expression>),
|
||||||
/// Variable
|
/// Variable
|
||||||
Var(String),
|
Var(String),
|
||||||
/// Binary operation. Consists of type, left hand side and right hand side
|
/// Binary operation. Consists of type, left hand side and right hand side
|
||||||
@@ -116,35 +118,11 @@ pub enum Statement {
|
|||||||
Loop(Loop),
|
Loop(Loop),
|
||||||
If(If),
|
If(If),
|
||||||
Print(Expression),
|
Print(Expression),
|
||||||
|
FunDecl(String, Vec<String>, Ast),
|
||||||
|
Return(Expression),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Default)]
|
#[derive(Debug, PartialEq, Eq, Clone, Default)]
|
||||||
pub struct Ast {
|
pub struct Ast {
|
||||||
pub prog: Vec<Statement>,
|
pub prog: Vec<Statement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BinOpType {
|
|
||||||
/// Get the precedence for a binary operator. Higher value means the OP is stronger binding.
|
|
||||||
/// For example Multiplication is stronger than addition, so Mul has higher precedence than Add.
|
|
||||||
///
|
|
||||||
/// The operator precedences are derived from the C language operator precedences. While not all
|
|
||||||
/// C operators are included or the exact same, the precedence oder is the same.
|
|
||||||
/// See: https://en.cppreference.com/w/c/language/operator_precedence
|
|
||||||
|
|
||||||
pub fn precedence(&self) -> u8 {
|
|
||||||
match self {
|
|
||||||
BinOpType::Declare => 0,
|
|
||||||
BinOpType::Assign => 1,
|
|
||||||
BinOpType::LOr => 2,
|
|
||||||
BinOpType::LAnd => 3,
|
|
||||||
BinOpType::BOr => 4,
|
|
||||||
BinOpType::BXor => 5,
|
|
||||||
BinOpType::BAnd => 6,
|
|
||||||
BinOpType::EquEqu | BinOpType::NotEqu => 7,
|
|
||||||
BinOpType::Less | BinOpType::LessEqu | BinOpType::Greater | BinOpType::GreaterEqu => 8,
|
|
||||||
BinOpType::Shl | BinOpType::Shr => 9,
|
|
||||||
BinOpType::Add | BinOpType::Sub => 10,
|
|
||||||
BinOpType::Mul | BinOpType::Div | BinOpType::Mod => 11,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
use std::{fmt::Display, rc::Rc};
|
use std::{collections::HashMap, fmt::Display, rc::Rc, cell::RefCell};
|
||||||
|
|
||||||
use crate::{
|
use crate::{ast::{Expression, BinOpType, UnOpType, Ast, Statement, If}, parser::parse, lexer::lex};
|
||||||
ast::{Ast, BinOpType, Expression, If, Statement, UnOpType},
|
|
||||||
lexer::lex,
|
|
||||||
parser::parse,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
@@ -12,34 +8,25 @@ pub enum Value {
|
|||||||
String(Rc<String>),
|
String(Rc<String>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum RunEnd {
|
||||||
|
Return(Value),
|
||||||
|
End,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Interpreter {
|
pub struct Interpreter {
|
||||||
// Variable table stores the runtime values of variables
|
// Variable table stores the runtime values of variables
|
||||||
vartable: Vec<(String, Value)>,
|
vartable: HashMap<String, Value>,
|
||||||
|
funtable: HashMap<String, RefCell<(Vec<String>, Ast)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpreter {
|
impl Interpreter {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
vartable: Vec::new(),
|
vartable: HashMap::new(),
|
||||||
|
funtable: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_var(&self, name: &str) -> Option<Value> {
|
|
||||||
self.vartable
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.find(|it| it.0 == name)
|
|
||||||
.map(|it| it.1.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_var_mut(&mut self, name: &str) -> Option<&mut Value> {
|
|
||||||
self.vartable
|
|
||||||
.iter_mut()
|
|
||||||
.rev()
|
|
||||||
.find(|it| it.0 == name)
|
|
||||||
.map(|it| &mut it.1)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_str(&mut self, code: &str, print_tokens: bool, print_ast: bool) {
|
pub fn run_str(&mut self, code: &str, print_tokens: bool, print_ast: bool) {
|
||||||
let tokens = lex(code).unwrap();
|
let tokens = lex(code).unwrap();
|
||||||
if print_tokens {
|
if print_tokens {
|
||||||
@@ -54,14 +41,17 @@ impl Interpreter {
|
|||||||
self.run(&ast);
|
self.run(&ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self, prog: &Ast) {
|
pub fn run(&mut self, prog: &Ast) -> RunEnd {
|
||||||
let vartable_len = self.vartable.len();
|
|
||||||
for stmt in &prog.prog {
|
for stmt in &prog.prog {
|
||||||
match stmt {
|
match stmt {
|
||||||
Statement::Expr(expr) => {
|
Statement::Expr(expr) => {
|
||||||
self.resolve_expr(expr);
|
self.resolve_expr(expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Statement::Return(expr) => {
|
||||||
|
return RunEnd::Return(self.resolve_expr(expr));
|
||||||
|
}
|
||||||
|
|
||||||
Statement::Loop(looop) => {
|
Statement::Loop(looop) => {
|
||||||
// loop runs as long condition != 0
|
// loop runs as long condition != 0
|
||||||
loop {
|
loop {
|
||||||
@@ -69,7 +59,10 @@ impl Interpreter {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.run(&looop.body);
|
match self.run(&looop.body) {
|
||||||
|
RunEnd::Return(val) => return RunEnd::Return(val),
|
||||||
|
RunEnd::End => (),
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(adv) = &looop.advancement {
|
if let Some(adv) = &looop.advancement {
|
||||||
self.resolve_expr(&adv);
|
self.resolve_expr(&adv);
|
||||||
@@ -82,21 +75,24 @@ impl Interpreter {
|
|||||||
print!("{}", result);
|
print!("{}", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
Statement::If(If {
|
Statement::If(If {condition, body_true, body_false}) => {
|
||||||
condition,
|
let end = if matches!(self.resolve_expr(condition), Value::I64(0)) {
|
||||||
body_true,
|
self.run(body_false)
|
||||||
body_false,
|
|
||||||
}) => {
|
|
||||||
if matches!(self.resolve_expr(condition), Value::I64(0)) {
|
|
||||||
self.run(body_false);
|
|
||||||
} else {
|
} else {
|
||||||
self.run(body_true);
|
self.run(body_true)
|
||||||
|
};
|
||||||
|
match end {
|
||||||
|
RunEnd::Return(val) => return RunEnd::Return(val),
|
||||||
|
RunEnd::End => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Statement::FunDecl(name, args, body) => {
|
||||||
|
self.funtable.insert(name.clone(), (args.clone(), body.clone()).into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.vartable.truncate(vartable_len);
|
RunEnd::End
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_expr(&mut self, expr: &Expression) -> Value {
|
fn resolve_expr(&mut self, expr: &Expression) -> Value {
|
||||||
@@ -106,11 +102,28 @@ impl Interpreter {
|
|||||||
Expression::BinOp(bo, lhs, rhs) => self.resolve_binop(bo, lhs, rhs),
|
Expression::BinOp(bo, lhs, rhs) => self.resolve_binop(bo, lhs, rhs),
|
||||||
Expression::UnOp(uo, operand) => self.resolve_unop(uo, operand),
|
Expression::UnOp(uo, operand) => self.resolve_unop(uo, operand),
|
||||||
Expression::Var(name) => self.resolve_var(name),
|
Expression::Var(name) => self.resolve_var(name),
|
||||||
|
Expression::FunCall(name, args) => {
|
||||||
|
let fun = self.funtable.get(name).expect("Function not declared").clone();
|
||||||
|
for i in 0 .. args.len() {
|
||||||
|
let val = self.resolve_expr(&args[i]);
|
||||||
|
self.vartable.insert(fun.borrow().0[i].clone(), val);
|
||||||
|
}
|
||||||
|
|
||||||
|
if fun.borrow().0.len() != args.len() {
|
||||||
|
panic!("Invalid number of arguments for function");
|
||||||
|
}
|
||||||
|
|
||||||
|
let end = self.run(&fun.borrow().1);
|
||||||
|
match end {
|
||||||
|
RunEnd::Return(val) => val,
|
||||||
|
RunEnd::End => Value::I64(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_var(&mut self, name: &str) -> Value {
|
fn resolve_var(&mut self, name: &str) -> Value {
|
||||||
match self.get_var(name) {
|
match self.vartable.get(name) {
|
||||||
Some(val) => val.clone(),
|
Some(val) => val.clone(),
|
||||||
None => panic!("Variable '{}' used but not declared", name),
|
None => panic!("Variable '{}' used but not declared", name),
|
||||||
}
|
}
|
||||||
@@ -132,19 +145,19 @@ impl Interpreter {
|
|||||||
|
|
||||||
match (&bo, &lhs) {
|
match (&bo, &lhs) {
|
||||||
(BinOpType::Declare, Expression::Var(name)) => {
|
(BinOpType::Declare, Expression::Var(name)) => {
|
||||||
self.vartable.push((name.clone(), rhs.clone()));
|
self.vartable.insert(name.clone(), rhs.clone());
|
||||||
return rhs;
|
return rhs;
|
||||||
}
|
}
|
||||||
(BinOpType::Assign, Expression::Var(name)) => {
|
(BinOpType::Assign, Expression::Var(name)) => {
|
||||||
match self.get_var_mut(name) {
|
match self.vartable.get_mut(name) {
|
||||||
Some(val) => *val = rhs.clone(),
|
Some(val) => *val = rhs.clone(),
|
||||||
None => panic!("Runtime Error: Trying to assign value to undeclared variable"),
|
None => panic!("Runtime Error: Trying to assign value to undeclared variable"),
|
||||||
}
|
}
|
||||||
return rhs;
|
return rhs;
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => ()
|
||||||
}
|
}
|
||||||
|
|
||||||
let lhs = self.resolve_expr(lhs);
|
let lhs = self.resolve_expr(lhs);
|
||||||
|
|
||||||
match (lhs, rhs) {
|
match (lhs, rhs) {
|
||||||
@@ -184,10 +197,11 @@ impl Display for Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::{Interpreter, Value};
|
use super::{Interpreter, Value};
|
||||||
use crate::ast::{BinOpType, Expression};
|
use crate::ast::{Expression, BinOpType};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_interpreter_expr() {
|
fn test_interpreter_expr() {
|
||||||
@@ -198,12 +212,7 @@ mod test {
|
|||||||
Expression::BinOp(
|
Expression::BinOp(
|
||||||
BinOpType::Add,
|
BinOpType::Add,
|
||||||
Expression::I64(1).into(),
|
Expression::I64(1).into(),
|
||||||
Expression::BinOp(
|
Expression::BinOp(BinOpType::Mul, Expression::I64(2).into(), Expression::I64(3).into()).into(),
|
||||||
BinOpType::Mul,
|
|
||||||
Expression::I64(2).into(),
|
|
||||||
Expression::I64(3).into(),
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
Expression::I64(4).into(),
|
Expression::I64(4).into(),
|
||||||
|
|||||||
187
src/lexer.rs
187
src/lexer.rs
@@ -106,17 +106,99 @@ impl<'a> Lexer<'a> {
|
|||||||
'{' => tokens.push(Token::LBraces),
|
'{' => tokens.push(Token::LBraces),
|
||||||
'}' => tokens.push(Token::RBraces),
|
'}' => tokens.push(Token::RBraces),
|
||||||
'!' => tokens.push(Token::LNot),
|
'!' => tokens.push(Token::LNot),
|
||||||
|
',' => tokens.push(Token::Comma),
|
||||||
|
|
||||||
// Special tokens with variable length
|
// Lex numbers
|
||||||
|
ch @ '0'..='9' => {
|
||||||
|
// String representation of the integer value
|
||||||
|
let mut sval = String::from(ch);
|
||||||
|
|
||||||
// Lex multiple characters together as numbers
|
// Do as long as a next char exists and it is a numeric char
|
||||||
ch @ '0'..='9' => tokens.push(self.lex_number(ch)?),
|
loop {
|
||||||
|
// The next char is verified to be Some, so unwrap is safe
|
||||||
|
match self.peek() {
|
||||||
|
// Underscore is a separator, so remove it but don't add to number
|
||||||
|
'_' => {
|
||||||
|
self.next();
|
||||||
|
}
|
||||||
|
'0'..='9' => {
|
||||||
|
sval.push(self.next());
|
||||||
|
}
|
||||||
|
// Next char is not a number, so stop and finish the number token
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Lex multiple characters together as a string
|
// Try to convert the string representation of the value to i64
|
||||||
'"' => tokens.push(self.lex_str()?),
|
let i64val = sval.parse().map_err(|_| LexErr::NumericParse(sval))?;
|
||||||
|
tokens.push(Token::I64(i64val));
|
||||||
|
}
|
||||||
|
|
||||||
// Lex multiple characters together as identifier
|
// Lex a string
|
||||||
ch @ ('a'..='z' | 'A'..='Z' | '_') => tokens.push(self.lex_identifier(ch)?),
|
'"' => {
|
||||||
|
// Opening " was consumed in match
|
||||||
|
|
||||||
|
let mut text = String::new();
|
||||||
|
|
||||||
|
// Read all chars until encountering the closing "
|
||||||
|
loop {
|
||||||
|
match self.peek() {
|
||||||
|
'"' => break,
|
||||||
|
// If the end of file is reached while still waiting for '"', error out
|
||||||
|
'\0' => Err(LexErr::MissingClosingString)?,
|
||||||
|
_ => match self.next() {
|
||||||
|
// Backshlash indicates an escaped character
|
||||||
|
'\\' => match self.next() {
|
||||||
|
'n' => text.push('\n'),
|
||||||
|
'r' => text.push('\r'),
|
||||||
|
't' => text.push('\t'),
|
||||||
|
'\\' => text.push('\\'),
|
||||||
|
'"' => text.push('"'),
|
||||||
|
ch => Err(LexErr::InvalidStrEscape(ch))?,
|
||||||
|
},
|
||||||
|
// All other characters are simply appended to the string
|
||||||
|
ch => text.push(ch),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consume closing "
|
||||||
|
self.next();
|
||||||
|
|
||||||
|
tokens.push(Token::String(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lex characters as identifier
|
||||||
|
ch @ ('a'..='z' | 'A'..='Z' | '_') => {
|
||||||
|
let mut ident = String::from(ch);
|
||||||
|
|
||||||
|
// Do as long as a next char exists and it is a valid char for an identifier
|
||||||
|
loop {
|
||||||
|
match self.peek() {
|
||||||
|
// In the middle of an identifier numbers are also allowed
|
||||||
|
'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => {
|
||||||
|
ident.push(self.next());
|
||||||
|
}
|
||||||
|
// Next char is not valid, so stop and finish the ident token
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for pre-defined keywords
|
||||||
|
let token = match ident.as_str() {
|
||||||
|
"loop" => Token::Loop,
|
||||||
|
"print" => Token::Print,
|
||||||
|
"if" => Token::If,
|
||||||
|
"else" => Token::Else,
|
||||||
|
"fun" => Token::Fun,
|
||||||
|
"return" => Token::Return,
|
||||||
|
|
||||||
|
// If it doesn't match a keyword, it is a normal identifier
|
||||||
|
_ => Token::Ident(ident),
|
||||||
|
};
|
||||||
|
|
||||||
|
tokens.push(token);
|
||||||
|
}
|
||||||
|
|
||||||
ch => Err(LexErr::UnexpectedChar(ch))?,
|
ch => Err(LexErr::UnexpectedChar(ch))?,
|
||||||
}
|
}
|
||||||
@@ -125,97 +207,6 @@ impl<'a> Lexer<'a> {
|
|||||||
Ok(tokens)
|
Ok(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lex multiple characters as a number until encountering a non numeric digit. This includes
|
|
||||||
/// the first character
|
|
||||||
fn lex_number(&mut self, first_char: char) -> Result<Token, LexErr> {
|
|
||||||
// String representation of the integer value
|
|
||||||
let mut sval = String::from(first_char);
|
|
||||||
|
|
||||||
// Do as long as a next char exists and it is a numeric char
|
|
||||||
loop {
|
|
||||||
// The next char is verified to be Some, so unwrap is safe
|
|
||||||
match self.peek() {
|
|
||||||
// Underscore is a separator, so remove it but don't add to number
|
|
||||||
'_' => {
|
|
||||||
self.next();
|
|
||||||
}
|
|
||||||
'0'..='9' => {
|
|
||||||
sval.push(self.next());
|
|
||||||
}
|
|
||||||
// Next char is not a number, so stop and finish the number token
|
|
||||||
_ => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to convert the string representation of the value to i64
|
|
||||||
let i64val = sval.parse().map_err(|_| LexErr::NumericParse(sval))?;
|
|
||||||
Ok(Token::I64(i64val))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lex characters as a string until encountering an unescaped closing doublequoute char '"'
|
|
||||||
fn lex_str(&mut self) -> Result<Token, LexErr> {
|
|
||||||
// Opening " was consumed in match
|
|
||||||
|
|
||||||
let mut text = String::new();
|
|
||||||
|
|
||||||
// Read all chars until encountering the closing "
|
|
||||||
loop {
|
|
||||||
match self.peek() {
|
|
||||||
'"' => break,
|
|
||||||
// If the end of file is reached while still waiting for '"', error out
|
|
||||||
'\0' => Err(LexErr::MissingClosingString)?,
|
|
||||||
_ => match self.next() {
|
|
||||||
// Backshlash indicates an escaped character
|
|
||||||
'\\' => match self.next() {
|
|
||||||
'n' => text.push('\n'),
|
|
||||||
'r' => text.push('\r'),
|
|
||||||
't' => text.push('\t'),
|
|
||||||
'\\' => text.push('\\'),
|
|
||||||
'"' => text.push('"'),
|
|
||||||
ch => Err(LexErr::InvalidStrEscape(ch))?,
|
|
||||||
},
|
|
||||||
// All other characters are simply appended to the string
|
|
||||||
ch => text.push(ch),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consume closing "
|
|
||||||
self.next();
|
|
||||||
|
|
||||||
Ok(Token::String(text))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lex characters from the text as an identifier. This includes the first character passed in
|
|
||||||
fn lex_identifier(&mut self, first_char: char) -> Result<Token, LexErr> {
|
|
||||||
let mut ident = String::from(first_char);
|
|
||||||
|
|
||||||
// Do as long as a next char exists and it is a valid char for an identifier
|
|
||||||
loop {
|
|
||||||
match self.peek() {
|
|
||||||
// In the middle of an identifier numbers are also allowed
|
|
||||||
'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => {
|
|
||||||
ident.push(self.next());
|
|
||||||
}
|
|
||||||
// Next char is not valid, so stop and finish the ident token
|
|
||||||
_ => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for pre-defined keywords
|
|
||||||
let token = match ident.as_str() {
|
|
||||||
"loop" => Token::Loop,
|
|
||||||
"print" => Token::Print,
|
|
||||||
"if" => Token::If,
|
|
||||||
"else" => Token::Else,
|
|
||||||
|
|
||||||
// If it doesn't match a keyword, it is a normal identifier
|
|
||||||
_ => Token::Ident(ident),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(token)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Advance to next character and return the removed char
|
/// Advance to next character and return the removed char
|
||||||
fn next(&mut self) -> char {
|
fn next(&mut self) -> char {
|
||||||
self.code.next().unwrap_or('\0')
|
self.code.next().unwrap_or('\0')
|
||||||
|
|||||||
12
src/main.rs
12
src/main.rs
@@ -1,11 +1,8 @@
|
|||||||
use std::{
|
use std::{env::args, fs, io::{stdout, Write, stdin}};
|
||||||
env::args,
|
|
||||||
fs,
|
|
||||||
io::{stdin, stdout, Write},
|
|
||||||
};
|
|
||||||
|
|
||||||
use nek_lang::interpreter::Interpreter;
|
use nek_lang::interpreter::Interpreter;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct CliConfig {
|
struct CliConfig {
|
||||||
print_tokens: bool,
|
print_tokens: bool,
|
||||||
@@ -15,6 +12,7 @@ struct CliConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
||||||
let mut conf = CliConfig::default();
|
let mut conf = CliConfig::default();
|
||||||
|
|
||||||
// Go through all commandline arguments except the first (filename)
|
// Go through all commandline arguments except the first (filename)
|
||||||
@@ -44,12 +42,14 @@ fn main() {
|
|||||||
|
|
||||||
code.clear();
|
code.clear();
|
||||||
stdin().read_line(&mut code).unwrap();
|
stdin().read_line(&mut code).unwrap();
|
||||||
|
|
||||||
if code.trim() == "exit" {
|
if code.trim() == "exit" {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
interpreter.run_str(&code, conf.print_tokens, conf.print_ast);
|
interpreter.run_str(&code, conf.print_tokens, conf.print_ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
124
src/parser.rs
124
src/parser.rs
@@ -3,12 +3,6 @@ use std::iter::Peekable;
|
|||||||
use crate::ast::*;
|
use crate::ast::*;
|
||||||
use crate::token::Token;
|
use crate::token::Token;
|
||||||
|
|
||||||
/// Parse the given tokens into an abstract syntax tree
|
|
||||||
pub fn parse<T: Iterator<Item = Token>, A: IntoIterator<IntoIter = T>>(tokens: A) -> Ast {
|
|
||||||
let mut parser = Parser::new(tokens);
|
|
||||||
parser.parse()
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Parser<T: Iterator<Item = Token>> {
|
struct Parser<T: Iterator<Item = Token>> {
|
||||||
tokens: Peekable<T>,
|
tokens: Peekable<T>,
|
||||||
}
|
}
|
||||||
@@ -20,8 +14,6 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
Self { tokens }
|
Self { tokens }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse tokens into an abstract syntax tree. This will continuously parse statements until
|
|
||||||
/// encountering end-of-file or a block end '}' .
|
|
||||||
fn parse(&mut self) -> Ast {
|
fn parse(&mut self) -> Ast {
|
||||||
let mut prog = Vec::new();
|
let mut prog = Vec::new();
|
||||||
|
|
||||||
@@ -30,7 +22,10 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
Token::Semicolon => {
|
Token::Semicolon => {
|
||||||
self.next();
|
self.next();
|
||||||
}
|
}
|
||||||
Token::EoF | Token::RBraces => break,
|
Token::EoF => break,
|
||||||
|
Token::RBraces => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// By default try to lex a statement
|
// By default try to lex a statement
|
||||||
_ => prog.push(self.parse_stmt()),
|
_ => prog.push(self.parse_stmt()),
|
||||||
@@ -40,7 +35,6 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
Ast { prog }
|
Ast { prog }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a single statement from the tokens.
|
|
||||||
fn parse_stmt(&mut self) -> Statement {
|
fn parse_stmt(&mut self) -> Statement {
|
||||||
match self.peek() {
|
match self.peek() {
|
||||||
Token::Loop => Statement::Loop(self.parse_loop()),
|
Token::Loop => Statement::Loop(self.parse_loop()),
|
||||||
@@ -58,8 +52,64 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
Statement::Print(expr)
|
Statement::Print(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Token::Return => {
|
||||||
|
self.next();
|
||||||
|
|
||||||
|
let expr = self.parse_expr();
|
||||||
|
|
||||||
|
// After a statement, there must be a semicolon
|
||||||
|
if !matches!(self.next(), Token::Semicolon) {
|
||||||
|
panic!("Expected semicolon after statement");
|
||||||
|
}
|
||||||
|
|
||||||
|
Statement::Return(expr)
|
||||||
|
}
|
||||||
|
|
||||||
Token::If => Statement::If(self.parse_if()),
|
Token::If => Statement::If(self.parse_if()),
|
||||||
|
|
||||||
|
Token::Fun => {
|
||||||
|
self.next();
|
||||||
|
|
||||||
|
let name = match self.next() {
|
||||||
|
Token::Ident(name) => name,
|
||||||
|
_ => panic!("Error lexing function: Expected ident token"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut args = Vec::new();
|
||||||
|
|
||||||
|
if !matches!(self.next(), Token::LParen) {
|
||||||
|
panic!("Expected opening parenthesis");
|
||||||
|
}
|
||||||
|
|
||||||
|
while self.peek() != &Token::RParen {
|
||||||
|
let argname = match self.next() {
|
||||||
|
Token::Ident(argname) => argname,
|
||||||
|
_ => panic!("Error lexing function: Expected ident token for argname"),
|
||||||
|
};
|
||||||
|
|
||||||
|
args.push(argname);
|
||||||
|
|
||||||
|
if self.peek() == &Token::Comma {
|
||||||
|
self.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
self.next();
|
||||||
|
|
||||||
|
if !matches!(self.next(), Token::LBraces) {
|
||||||
|
panic!("Expected opening braces");
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = self.parse();
|
||||||
|
|
||||||
|
if !matches!(self.next(), Token::RBraces) {
|
||||||
|
panic!("Expected closing braces");
|
||||||
|
}
|
||||||
|
|
||||||
|
Statement::FunDecl(name, args, body)
|
||||||
|
}
|
||||||
|
|
||||||
// If it is not a loop, try to lex as an expression
|
// If it is not a loop, try to lex as an expression
|
||||||
_ => {
|
_ => {
|
||||||
let stmt = Statement::Expr(self.parse_expr());
|
let stmt = Statement::Expr(self.parse_expr());
|
||||||
@@ -74,7 +124,6 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an if statement from the tokens
|
|
||||||
fn parse_if(&mut self) -> If {
|
fn parse_if(&mut self) -> If {
|
||||||
if !matches!(self.next(), Token::If) {
|
if !matches!(self.next(), Token::If) {
|
||||||
panic!("Error lexing if: Expected if token");
|
panic!("Error lexing if: Expected if token");
|
||||||
@@ -115,7 +164,6 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a loop statement from the tokens
|
|
||||||
fn parse_loop(&mut self) -> Loop {
|
fn parse_loop(&mut self) -> Loop {
|
||||||
if !matches!(self.next(), Token::Loop) {
|
if !matches!(self.next(), Token::Loop) {
|
||||||
panic!("Error lexing loop: Expected loop token");
|
panic!("Error lexing loop: Expected loop token");
|
||||||
@@ -155,7 +203,6 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a single expression from the tokens
|
|
||||||
fn parse_expr(&mut self) -> Expression {
|
fn parse_expr(&mut self) -> Expression {
|
||||||
let lhs = self.parse_primary();
|
let lhs = self.parse_primary();
|
||||||
self.parse_expr_precedence(lhs, 0)
|
self.parse_expr_precedence(lhs, 0)
|
||||||
@@ -198,6 +245,8 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
// Literal String
|
// Literal String
|
||||||
Token::String(text) => Expression::String(text.into()),
|
Token::String(text) => Expression::String(text.into()),
|
||||||
|
|
||||||
|
Token::Ident(name) if matches!(self.peek(), Token::LParen) => self.parse_funcall(name),
|
||||||
|
|
||||||
Token::Ident(name) => Expression::Var(name),
|
Token::Ident(name) => Expression::Var(name),
|
||||||
|
|
||||||
// Parentheses grouping
|
// Parentheses grouping
|
||||||
@@ -234,6 +283,24 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_funcall(&mut self, name: String) -> Expression {
|
||||||
|
let mut args = Vec::new();
|
||||||
|
|
||||||
|
// Consume (
|
||||||
|
self.next();
|
||||||
|
|
||||||
|
while self.peek() != &Token::RParen {
|
||||||
|
args.push(self.parse_expr());
|
||||||
|
|
||||||
|
if self.peek() == &Token::Comma {
|
||||||
|
self.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.next();
|
||||||
|
|
||||||
|
Expression::FunCall(name, args)
|
||||||
|
}
|
||||||
|
|
||||||
/// 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(&Token::EoF)
|
self.tokens.peek().unwrap_or(&Token::EoF)
|
||||||
@@ -245,6 +312,37 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse<T: Iterator<Item = Token>, A: IntoIterator<IntoIter = T>>(tokens: A) -> Ast {
|
||||||
|
let mut parser = Parser::new(tokens);
|
||||||
|
parser.parse()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BinOpType {
|
||||||
|
/// Get the precedence for a binary operator. Higher value means the OP is stronger binding.
|
||||||
|
/// For example Multiplication is stronger than addition, so Mul has higher precedence than Add.
|
||||||
|
///
|
||||||
|
/// The operator precedences are derived from the C language operator precedences. While not all
|
||||||
|
/// C operators are included or the exact same, the precedence oder is the same.
|
||||||
|
/// See: https://en.cppreference.com/w/c/language/operator_precedence
|
||||||
|
|
||||||
|
fn precedence(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
BinOpType::Declare => 0,
|
||||||
|
BinOpType::Assign => 1,
|
||||||
|
BinOpType::LOr => 2,
|
||||||
|
BinOpType::LAnd => 3,
|
||||||
|
BinOpType::BOr => 4,
|
||||||
|
BinOpType::BXor => 5,
|
||||||
|
BinOpType::BAnd => 6,
|
||||||
|
BinOpType::EquEqu | BinOpType::NotEqu => 7,
|
||||||
|
BinOpType::Less | BinOpType::LessEqu | BinOpType::Greater | BinOpType::GreaterEqu => 8,
|
||||||
|
BinOpType::Shl | BinOpType::Shr => 9,
|
||||||
|
BinOpType::Add | BinOpType::Sub => 10,
|
||||||
|
BinOpType::Mul | BinOpType::Div | BinOpType::Mod => 11,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{parse, BinOpType, Expression};
|
use super::{parse, BinOpType, Expression};
|
||||||
|
|||||||
@@ -23,6 +23,12 @@ pub enum Token {
|
|||||||
/// Else keyword (else)
|
/// Else keyword (else)
|
||||||
Else,
|
Else,
|
||||||
|
|
||||||
|
Fun,
|
||||||
|
|
||||||
|
Comma,
|
||||||
|
|
||||||
|
Return,
|
||||||
|
|
||||||
/// Left Parenthesis ('(')
|
/// Left Parenthesis ('(')
|
||||||
LParen,
|
LParen,
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user