Compare commits
41 Commits
feature-fu
...
vec-based-
| Author | SHA1 | Date | |
|---|---|---|---|
| 21228ff3d7 | |||
| 588b3b5b2c | |||
| f6152670aa | |||
| c2b9ee71b8 | |||
| f8e5bd7423 | |||
| d7001a5c52 | |||
| bc68d9fa49 | |||
| 264d8f92f4 | |||
| d8f5b876ac | |||
| 8cf6177cbc | |||
| 39bd4400b4 | |||
| 75b99869d4 | |||
| de0bbb8171 | |||
| 92f59cbf9a | |||
| dd9ca660cc | |||
| 7e2ef49481 | |||
| 86130984e2 | |||
| c4b146c325 | |||
| 7b6fc89fb7 | |||
| 8c9756b6d2 | |||
| 02993142df | |||
| 3348b7cf6d | |||
| 3098dc7e0a | |||
| e0c00019ff | |||
| 35fbae8ab9 | |||
| 23d336d63e | |||
| 39351e1131 | |||
| b7872da3ea | |||
| 5cc89b855a | |||
| 32e4f1ea4f | |||
| b664297c73 | |||
| ea60f17647 | |||
| 5ffa0ea2ec | |||
| 2a59fe8c84 | |||
| 8f79440219 | |||
| 128b05b8a8 | |||
| a9ee8eb66c | |||
| 5c7b6a7b41 | |||
| a569781691 | |||
| 0b75c30784 | |||
| ed2ae144dd |
28
examples/euler5.nek
Normal file
28
examples/euler5.nek
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// 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,8 +82,6 @@ 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
|
||||||
@@ -118,11 +116,35 @@ 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,6 +1,10 @@
|
|||||||
use std::{collections::HashMap, fmt::Display, rc::Rc, cell::RefCell};
|
use std::{fmt::Display, rc::Rc};
|
||||||
|
|
||||||
use crate::{ast::{Expression, BinOpType, UnOpType, Ast, Statement, If}, parser::parse, lexer::lex};
|
use crate::{
|
||||||
|
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 {
|
||||||
@@ -8,25 +12,34 @@ 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: HashMap<String, Value>,
|
vartable: Vec<(String, Value)>,
|
||||||
funtable: HashMap<String, RefCell<(Vec<String>, Ast)>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpreter {
|
impl Interpreter {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
vartable: HashMap::new(),
|
vartable: Vec::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 {
|
||||||
@@ -41,17 +54,14 @@ impl Interpreter {
|
|||||||
self.run(&ast);
|
self.run(&ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self, prog: &Ast) -> RunEnd {
|
pub fn run(&mut self, prog: &Ast) {
|
||||||
|
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 {
|
||||||
@@ -59,10 +69,7 @@ impl Interpreter {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.run(&looop.body) {
|
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);
|
||||||
@@ -75,24 +82,21 @@ impl Interpreter {
|
|||||||
print!("{}", result);
|
print!("{}", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
Statement::If(If {condition, body_true, body_false}) => {
|
Statement::If(If {
|
||||||
let end = if matches!(self.resolve_expr(condition), Value::I64(0)) {
|
condition,
|
||||||
self.run(body_false)
|
body_true,
|
||||||
|
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RunEnd::End
|
self.vartable.truncate(vartable_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_expr(&mut self, expr: &Expression) -> Value {
|
fn resolve_expr(&mut self, expr: &Expression) -> Value {
|
||||||
@@ -102,28 +106,11 @@ 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.vartable.get(name) {
|
match self.get_var(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),
|
||||||
}
|
}
|
||||||
@@ -145,19 +132,19 @@ impl Interpreter {
|
|||||||
|
|
||||||
match (&bo, &lhs) {
|
match (&bo, &lhs) {
|
||||||
(BinOpType::Declare, Expression::Var(name)) => {
|
(BinOpType::Declare, Expression::Var(name)) => {
|
||||||
self.vartable.insert(name.clone(), rhs.clone());
|
self.vartable.push((name.clone(), rhs.clone()));
|
||||||
return rhs;
|
return rhs;
|
||||||
}
|
}
|
||||||
(BinOpType::Assign, Expression::Var(name)) => {
|
(BinOpType::Assign, Expression::Var(name)) => {
|
||||||
match self.vartable.get_mut(name) {
|
match self.get_var_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) {
|
||||||
@@ -197,11 +184,10 @@ impl Display for Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::{Interpreter, Value};
|
use super::{Interpreter, Value};
|
||||||
use crate::ast::{Expression, BinOpType};
|
use crate::ast::{BinOpType, Expression};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_interpreter_expr() {
|
fn test_interpreter_expr() {
|
||||||
@@ -212,7 +198,12 @@ mod test {
|
|||||||
Expression::BinOp(
|
Expression::BinOp(
|
||||||
BinOpType::Add,
|
BinOpType::Add,
|
||||||
Expression::I64(1).into(),
|
Expression::I64(1).into(),
|
||||||
Expression::BinOp(BinOpType::Mul, Expression::I64(2).into(), Expression::I64(3).into()).into(),
|
Expression::BinOp(
|
||||||
|
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,99 +106,17 @@ 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),
|
|
||||||
|
|
||||||
// Lex numbers
|
// Special tokens with variable length
|
||||||
ch @ '0'..='9' => {
|
|
||||||
// String representation of the integer value
|
|
||||||
let mut sval = String::from(ch);
|
|
||||||
|
|
||||||
// Do as long as a next char exists and it is a numeric char
|
// Lex multiple characters together as numbers
|
||||||
loop {
|
ch @ '0'..='9' => tokens.push(self.lex_number(ch)?),
|
||||||
// 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
|
// Lex multiple characters together as a string
|
||||||
let i64val = sval.parse().map_err(|_| LexErr::NumericParse(sval))?;
|
'"' => tokens.push(self.lex_str()?),
|
||||||
tokens.push(Token::I64(i64val));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lex a string
|
// Lex multiple characters together as identifier
|
||||||
'"' => {
|
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))?,
|
||||||
}
|
}
|
||||||
@@ -207,6 +125,97 @@ 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,8 +1,11 @@
|
|||||||
use std::{env::args, fs, io::{stdout, Write, stdin}};
|
use std::{
|
||||||
|
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,
|
||||||
@@ -12,7 +15,6 @@ 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)
|
||||||
@@ -42,14 +44,12 @@ 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,6 +3,12 @@ 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>,
|
||||||
}
|
}
|
||||||
@@ -14,6 +20,8 @@ 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();
|
||||||
|
|
||||||
@@ -22,10 +30,7 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
Token::Semicolon => {
|
Token::Semicolon => {
|
||||||
self.next();
|
self.next();
|
||||||
}
|
}
|
||||||
Token::EoF => break,
|
Token::EoF | Token::RBraces => 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()),
|
||||||
@@ -35,6 +40,7 @@ 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()),
|
||||||
@@ -52,64 +58,8 @@ 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());
|
||||||
@@ -124,6 +74,7 @@ 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");
|
||||||
@@ -164,6 +115,7 @@ 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");
|
||||||
@@ -203,6 +155,7 @@ 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)
|
||||||
@@ -245,8 +198,6 @@ 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
|
||||||
@@ -283,24 +234,6 @@ 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)
|
||||||
@@ -312,37 +245,6 @@ 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,12 +23,6 @@ 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