Implement variables
- Assignment - Declaration - Identifier lexing
This commit is contained in:
parent
39351e1131
commit
23d336d63e
@ -33,9 +33,9 @@
|
|||||||
- [x] Bitwise NOT `~X`
|
- [x] Bitwise NOT `~X`
|
||||||
- [x] Bitwise left shift `X<<Y`
|
- [x] Bitwise left shift `X<<Y`
|
||||||
- [x] Bitwise right shift `X>>Y`
|
- [x] Bitwise right shift `X>>Y`
|
||||||
- [ ] Variables
|
- [x] Variables
|
||||||
- [ ] Declaration
|
- [x] Declaration
|
||||||
- [ ] Assignment
|
- [x] Assignment
|
||||||
- [ ] Control flow
|
- [ ] Control flow
|
||||||
- [ ] While loop `while X { ... }`
|
- [ ] While loop `while X { ... }`
|
||||||
- [ ] If else statement `if X { ... } else { ... }`
|
- [ ] If else statement `if X { ... } else { ... }`
|
||||||
@ -52,7 +52,7 @@
|
|||||||
### Expressions
|
### Expressions
|
||||||
|
|
||||||
```
|
```
|
||||||
expr_primary = LITERAL | "(" expr p | "-" expr_primary | "~" expr_primary
|
expr_primary = LITERAL | IDENT | "(" expr ")" | "-" expr_primary | "~" expr_primary
|
||||||
expr_mul = expr_primary (("*" | "/" | "%") expr_primary)*
|
expr_mul = expr_primary (("*" | "/" | "%") expr_primary)*
|
||||||
expr_add = expr_mul (("+" | "-") expr_mul)*
|
expr_add = expr_mul (("+" | "-") expr_mul)*
|
||||||
expr_shift = expr_add ((">>" | "<<") expr_add)*
|
expr_shift = expr_add ((">>" | "<<") expr_add)*
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::parser::{Ast, BinOpType, UnOpType};
|
use crate::parser::{Ast, BinOpType, UnOpType};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
@ -6,12 +8,15 @@ pub enum Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Interpreter {
|
pub struct Interpreter {
|
||||||
// Runtime storage, for example variables ...
|
// Variable table stores the runtime values of variables
|
||||||
|
vartable: HashMap<String, Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpreter {
|
impl Interpreter {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {}
|
Self {
|
||||||
|
vartable: HashMap::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self, prog: Ast) {
|
pub fn run(&mut self, prog: Ast) {
|
||||||
@ -25,6 +30,14 @@ impl Interpreter {
|
|||||||
Ast::I64(val) => Value::I64(val),
|
Ast::I64(val) => Value::I64(val),
|
||||||
Ast::BinOp(bo, lhs, rhs) => self.resolve_binop(bo, *lhs, *rhs),
|
Ast::BinOp(bo, lhs, rhs) => self.resolve_binop(bo, *lhs, *rhs),
|
||||||
Ast::UnOp(uo, operand) => self.resolve_unop(uo, *operand),
|
Ast::UnOp(uo, operand) => self.resolve_unop(uo, *operand),
|
||||||
|
Ast::Var(name) => self.resolve_var(name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_var(&mut self, name: String) -> Value {
|
||||||
|
match self.vartable.get(&name) {
|
||||||
|
Some(val) => val.clone(),
|
||||||
|
None => panic!("Variable '{}' used but not declared", name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,9 +52,25 @@ impl Interpreter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_binop(&mut self, bo: BinOpType, lhs: Ast, rhs: Ast) -> Value {
|
fn resolve_binop(&mut self, bo: BinOpType, lhs: Ast, rhs: Ast) -> Value {
|
||||||
let lhs = self.resolve_expr(lhs);
|
|
||||||
let rhs = self.resolve_expr(rhs);
|
let rhs = self.resolve_expr(rhs);
|
||||||
|
|
||||||
|
match (&bo, &lhs) {
|
||||||
|
(BinOpType::Declare, Ast::Var(name)) => {
|
||||||
|
self.vartable.insert(name.clone(), rhs.clone());
|
||||||
|
return rhs;
|
||||||
|
}
|
||||||
|
(BinOpType::Assign, Ast::Var(name)) => {
|
||||||
|
match self.vartable.get_mut(name) {
|
||||||
|
Some(val) => *val = rhs.clone(),
|
||||||
|
None => panic!("Runtime Error: Trying to assign value to undeclared variable"),
|
||||||
|
}
|
||||||
|
return rhs;
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
|
||||||
|
let lhs = self.resolve_expr(lhs);
|
||||||
|
|
||||||
match (lhs, rhs) {
|
match (lhs, rhs) {
|
||||||
(Value::I64(lhs), Value::I64(rhs)) => match bo {
|
(Value::I64(lhs), Value::I64(rhs)) => match bo {
|
||||||
BinOpType::Add => Value::I64(lhs + rhs),
|
BinOpType::Add => Value::I64(lhs + rhs),
|
||||||
@ -60,6 +89,8 @@ impl Interpreter {
|
|||||||
BinOpType::LessEqu => Value::I64(if lhs <= rhs { 1 } else { 0 }),
|
BinOpType::LessEqu => Value::I64(if lhs <= rhs { 1 } else { 0 }),
|
||||||
BinOpType::Greater => Value::I64(if lhs > rhs { 1 } else { 0 }),
|
BinOpType::Greater => Value::I64(if lhs > rhs { 1 } else { 0 }),
|
||||||
BinOpType::GreaterEqu => Value::I64(if lhs >= rhs { 1 } else { 0 }),
|
BinOpType::GreaterEqu => Value::I64(if lhs >= rhs { 1 } else { 0 }),
|
||||||
|
|
||||||
|
BinOpType::Declare | BinOpType::Assign => unreachable!(),
|
||||||
},
|
},
|
||||||
// _ => panic!("Value types are not compatible"),
|
// _ => panic!("Value types are not compatible"),
|
||||||
}
|
}
|
||||||
|
|||||||
83
src/lexer.rs
83
src/lexer.rs
@ -7,6 +7,9 @@ pub enum Token {
|
|||||||
/// Integer literal (64-bit)
|
/// Integer literal (64-bit)
|
||||||
I64(i64),
|
I64(i64),
|
||||||
|
|
||||||
|
/// Identifier (name for variables, functions, ...)
|
||||||
|
Ident(String),
|
||||||
|
|
||||||
/// Left Parenthesis ('(')
|
/// Left Parenthesis ('(')
|
||||||
LParen,
|
LParen,
|
||||||
|
|
||||||
@ -64,6 +67,12 @@ pub enum Token {
|
|||||||
/// Left angle bracket Equal (>=)
|
/// Left angle bracket Equal (>=)
|
||||||
RAngleEqu,
|
RAngleEqu,
|
||||||
|
|
||||||
|
/// Left arrow (<-)
|
||||||
|
LArrow,
|
||||||
|
|
||||||
|
/// Equal Sign (=)
|
||||||
|
Equ,
|
||||||
|
|
||||||
/// End of file
|
/// End of file
|
||||||
EoF,
|
EoF,
|
||||||
}
|
}
|
||||||
@ -89,30 +98,6 @@ impl<'a> Lexer<'a> {
|
|||||||
// Stop lexing at EOF
|
// Stop lexing at EOF
|
||||||
'\0' => break,
|
'\0' => break,
|
||||||
|
|
||||||
// Lex numbers
|
|
||||||
ch @ '0'..='9' => {
|
|
||||||
let mut sval = String::from(ch);
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: We only added numeric chars to the string, but the conversion could still fail
|
|
||||||
tokens.push(Token::I64(sval.parse().unwrap()));
|
|
||||||
}
|
|
||||||
|
|
||||||
'>' if matches!(self.peek(), '>') => {
|
'>' if matches!(self.peek(), '>') => {
|
||||||
self.next();
|
self.next();
|
||||||
tokens.push(Token::Shr);
|
tokens.push(Token::Shr);
|
||||||
@ -137,6 +122,10 @@ impl<'a> Lexer<'a> {
|
|||||||
self.next();
|
self.next();
|
||||||
tokens.push(Token::RAngleEqu);
|
tokens.push(Token::RAngleEqu);
|
||||||
}
|
}
|
||||||
|
'<' if matches!(self.peek(), '-') => {
|
||||||
|
self.next();
|
||||||
|
tokens.push(Token::LArrow);
|
||||||
|
}
|
||||||
|
|
||||||
'+' => tokens.push(Token::Add),
|
'+' => tokens.push(Token::Add),
|
||||||
'-' => tokens.push(Token::Sub),
|
'-' => tokens.push(Token::Sub),
|
||||||
@ -151,6 +140,49 @@ impl<'a> Lexer<'a> {
|
|||||||
'~' => tokens.push(Token::Tilde),
|
'~' => tokens.push(Token::Tilde),
|
||||||
'<' => tokens.push(Token::LAngle),
|
'<' => tokens.push(Token::LAngle),
|
||||||
'>' => tokens.push(Token::RAngle),
|
'>' => tokens.push(Token::RAngle),
|
||||||
|
'=' => tokens.push(Token::Equ),
|
||||||
|
|
||||||
|
// Lex numbers
|
||||||
|
ch @ '0'..='9' => {
|
||||||
|
let mut sval = String::from(ch);
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: We only added numeric chars to the string, but the conversion could still fail
|
||||||
|
tokens.push(Token::I64(sval.parse().unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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() {
|
||||||
|
'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => {
|
||||||
|
ident.push(self.next());
|
||||||
|
}
|
||||||
|
// Next char is not valid, so stop and finish the ident token
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens.push(Token::Ident(ident));
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: Don't panic, keep calm
|
//TODO: Don't panic, keep calm
|
||||||
ch => panic!("Lexer encountered unexpected char: '{}'", ch),
|
ch => panic!("Lexer encountered unexpected char: '{}'", ch),
|
||||||
@ -205,6 +237,9 @@ impl Token {
|
|||||||
Token::RAngle => BinOpType::Greater,
|
Token::RAngle => BinOpType::Greater,
|
||||||
Token::RAngleEqu => BinOpType::GreaterEqu,
|
Token::RAngleEqu => BinOpType::GreaterEqu,
|
||||||
|
|
||||||
|
Token::LArrow => BinOpType::Declare,
|
||||||
|
Token::Equ => BinOpType::Assign,
|
||||||
|
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/main.rs
12
src/main.rs
@ -1,10 +1,19 @@
|
|||||||
|
use std::io::Write;
|
||||||
|
|
||||||
use nek_lang::{lexer::lex, parser::parse, interpreter::Interpreter};
|
use nek_lang::{lexer::lex, parser::parse, interpreter::Interpreter};
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
||||||
|
let mut interpreter = Interpreter::new();
|
||||||
|
|
||||||
let mut code = String::new();
|
let mut code = String::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
print!(">> ");
|
||||||
|
std::io::stdout().flush().unwrap();
|
||||||
|
|
||||||
|
code.clear();
|
||||||
std::io::stdin().read_line(&mut code).unwrap();
|
std::io::stdin().read_line(&mut code).unwrap();
|
||||||
let code = code.trim();
|
let code = code.trim();
|
||||||
|
|
||||||
@ -16,8 +25,7 @@ fn main() {
|
|||||||
|
|
||||||
println!("Ast: {:#?}\n", ast);
|
println!("Ast: {:#?}\n", ast);
|
||||||
|
|
||||||
let mut interpreter = Interpreter::new();
|
|
||||||
|
|
||||||
interpreter.run(ast);
|
interpreter.run(ast);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -52,6 +52,12 @@ pub enum BinOpType {
|
|||||||
|
|
||||||
/// Shift Right
|
/// Shift Right
|
||||||
Shr,
|
Shr,
|
||||||
|
|
||||||
|
/// Assign value to variable
|
||||||
|
Assign,
|
||||||
|
|
||||||
|
/// Declare new variable with value
|
||||||
|
Declare,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
@ -67,6 +73,8 @@ pub enum UnOpType {
|
|||||||
pub enum Ast {
|
pub enum Ast {
|
||||||
/// Integer literal (64-bit)
|
/// Integer literal (64-bit)
|
||||||
I64(i64),
|
I64(i64),
|
||||||
|
/// Variable
|
||||||
|
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
|
||||||
BinOp(BinOpType, Box<Ast>, Box<Ast>),
|
BinOp(BinOpType, Box<Ast>, Box<Ast>),
|
||||||
/// Unary operation. Consists of type and operand
|
/// Unary operation. Consists of type and operand
|
||||||
@ -127,6 +135,8 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
// Literal i64
|
// Literal i64
|
||||||
Token::I64(val) => Ast::I64(val),
|
Token::I64(val) => Ast::I64(val),
|
||||||
|
|
||||||
|
Token::Ident(name) => Ast::Var(name),
|
||||||
|
|
||||||
// Parentheses grouping
|
// Parentheses grouping
|
||||||
Token::LParen => {
|
Token::LParen => {
|
||||||
let inner_expr = self.parse_expr();
|
let inner_expr = self.parse_expr();
|
||||||
@ -177,6 +187,7 @@ impl BinOpType {
|
|||||||
/// The operator precedences are derived from the C language operator precedences. While not all
|
/// 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.
|
/// C operators are included or the exact same, the precedence oder is the same.
|
||||||
/// See: https://en.cppreference.com/w/c/language/operator_precedence
|
/// See: https://en.cppreference.com/w/c/language/operator_precedence
|
||||||
|
|
||||||
fn precedence(&self) -> u8 {
|
fn precedence(&self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
BinOpType::BOr => 0,
|
BinOpType::BOr => 0,
|
||||||
@ -187,6 +198,8 @@ impl BinOpType {
|
|||||||
BinOpType::Shl | BinOpType::Shr => 5,
|
BinOpType::Shl | BinOpType::Shr => 5,
|
||||||
BinOpType::Add | BinOpType::Sub => 6,
|
BinOpType::Add | BinOpType::Sub => 6,
|
||||||
BinOpType::Mul | BinOpType::Div | BinOpType::Mod => 7,
|
BinOpType::Mul | BinOpType::Div | BinOpType::Mod => 7,
|
||||||
|
BinOpType::Assign => 8,
|
||||||
|
BinOpType::Declare => 9,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user