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 left shift `X<<Y`
|
||||
- [x] Bitwise right shift `X>>Y`
|
||||
- [ ] Variables
|
||||
- [ ] Declaration
|
||||
- [ ] Assignment
|
||||
- [x] Variables
|
||||
- [x] Declaration
|
||||
- [x] Assignment
|
||||
- [ ] Control flow
|
||||
- [ ] While loop `while X { ... }`
|
||||
- [ ] If else statement `if X { ... } else { ... }`
|
||||
@ -52,7 +52,7 @@
|
||||
### 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_add = expr_mul (("+" | "-") expr_mul)*
|
||||
expr_shift = expr_add ((">>" | "<<") expr_add)*
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::parser::{Ast, BinOpType, UnOpType};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
@ -6,12 +8,15 @@ pub enum Value {
|
||||
}
|
||||
|
||||
pub struct Interpreter {
|
||||
// Runtime storage, for example variables ...
|
||||
// Variable table stores the runtime values of variables
|
||||
vartable: HashMap<String, Value>,
|
||||
}
|
||||
|
||||
impl Interpreter {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
Self {
|
||||
vartable: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self, prog: Ast) {
|
||||
@ -25,6 +30,14 @@ impl Interpreter {
|
||||
Ast::I64(val) => Value::I64(val),
|
||||
Ast::BinOp(bo, lhs, rhs) => self.resolve_binop(bo, *lhs, *rhs),
|
||||
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 {
|
||||
let lhs = self.resolve_expr(lhs);
|
||||
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) {
|
||||
(Value::I64(lhs), Value::I64(rhs)) => match bo {
|
||||
BinOpType::Add => Value::I64(lhs + rhs),
|
||||
@ -60,6 +89,8 @@ impl Interpreter {
|
||||
BinOpType::LessEqu => 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::Declare | BinOpType::Assign => unreachable!(),
|
||||
},
|
||||
// _ => 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)
|
||||
I64(i64),
|
||||
|
||||
/// Identifier (name for variables, functions, ...)
|
||||
Ident(String),
|
||||
|
||||
/// Left Parenthesis ('(')
|
||||
LParen,
|
||||
|
||||
@ -64,6 +67,12 @@ pub enum Token {
|
||||
/// Left angle bracket Equal (>=)
|
||||
RAngleEqu,
|
||||
|
||||
/// Left arrow (<-)
|
||||
LArrow,
|
||||
|
||||
/// Equal Sign (=)
|
||||
Equ,
|
||||
|
||||
/// End of file
|
||||
EoF,
|
||||
}
|
||||
@ -89,30 +98,6 @@ impl<'a> Lexer<'a> {
|
||||
// Stop lexing at EOF
|
||||
'\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(), '>') => {
|
||||
self.next();
|
||||
tokens.push(Token::Shr);
|
||||
@ -137,6 +122,10 @@ impl<'a> Lexer<'a> {
|
||||
self.next();
|
||||
tokens.push(Token::RAngleEqu);
|
||||
}
|
||||
'<' if matches!(self.peek(), '-') => {
|
||||
self.next();
|
||||
tokens.push(Token::LArrow);
|
||||
}
|
||||
|
||||
'+' => tokens.push(Token::Add),
|
||||
'-' => tokens.push(Token::Sub),
|
||||
@ -151,6 +140,49 @@ impl<'a> Lexer<'a> {
|
||||
'~' => tokens.push(Token::Tilde),
|
||||
'<' => tokens.push(Token::LAngle),
|
||||
'>' => 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
|
||||
ch => panic!("Lexer encountered unexpected char: '{}'", ch),
|
||||
@ -205,6 +237,9 @@ impl Token {
|
||||
Token::RAngle => BinOpType::Greater,
|
||||
Token::RAngleEqu => BinOpType::GreaterEqu,
|
||||
|
||||
Token::LArrow => BinOpType::Declare,
|
||||
Token::Equ => BinOpType::Assign,
|
||||
|
||||
_ => 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};
|
||||
|
||||
|
||||
fn main() {
|
||||
|
||||
let mut interpreter = Interpreter::new();
|
||||
|
||||
let mut code = String::new();
|
||||
|
||||
loop {
|
||||
print!(">> ");
|
||||
std::io::stdout().flush().unwrap();
|
||||
|
||||
code.clear();
|
||||
std::io::stdin().read_line(&mut code).unwrap();
|
||||
let code = code.trim();
|
||||
|
||||
@ -16,8 +25,7 @@ fn main() {
|
||||
|
||||
println!("Ast: {:#?}\n", ast);
|
||||
|
||||
let mut interpreter = Interpreter::new();
|
||||
|
||||
interpreter.run(ast);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -52,6 +52,12 @@ pub enum BinOpType {
|
||||
|
||||
/// Shift Right
|
||||
Shr,
|
||||
|
||||
/// Assign value to variable
|
||||
Assign,
|
||||
|
||||
/// Declare new variable with value
|
||||
Declare,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
@ -67,6 +73,8 @@ pub enum UnOpType {
|
||||
pub enum Ast {
|
||||
/// Integer literal (64-bit)
|
||||
I64(i64),
|
||||
/// Variable
|
||||
Var(String),
|
||||
/// Binary operation. Consists of type, left hand side and right hand side
|
||||
BinOp(BinOpType, Box<Ast>, Box<Ast>),
|
||||
/// Unary operation. Consists of type and operand
|
||||
@ -127,6 +135,8 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
||||
// Literal i64
|
||||
Token::I64(val) => Ast::I64(val),
|
||||
|
||||
Token::Ident(name) => Ast::Var(name),
|
||||
|
||||
// Parentheses grouping
|
||||
Token::LParen => {
|
||||
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
|
||||
/// 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::BOr => 0,
|
||||
@ -187,6 +198,8 @@ impl BinOpType {
|
||||
BinOpType::Shl | BinOpType::Shr => 5,
|
||||
BinOpType::Add | BinOpType::Sub => 6,
|
||||
BinOpType::Mul | BinOpType::Div | BinOpType::Mod => 7,
|
||||
BinOpType::Assign => 8,
|
||||
BinOpType::Declare => 9,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user