Implement strings
This commit is contained in:
parent
39b55b51da
commit
abf9eb73c8
@ -1,10 +1,11 @@
|
|||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
use crate::{parser::{Expr, BinOpType, UnOpType, Ast, Stmt, parse}, lexer::lex};
|
use crate::{parser::{Expr, BinOpType, UnOpType, Ast, Stmt, parse}, lexer::lex};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
I64(i64),
|
I64(i64),
|
||||||
|
Str(Rc<String>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Interpreter {
|
pub struct Interpreter {
|
||||||
@ -50,6 +51,9 @@ impl Interpreter {
|
|||||||
match self.resolve_expr(condition) {
|
match self.resolve_expr(condition) {
|
||||||
Value::I64(val) if val == 0 => break,
|
Value::I64(val) if val == 0 => break,
|
||||||
Value::I64(_) => (),
|
Value::I64(_) => (),
|
||||||
|
|
||||||
|
Value::Str(text) if text.is_empty() => break,
|
||||||
|
Value::Str(_) => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute loop body
|
// Execute loop body
|
||||||
@ -70,6 +74,7 @@ impl Interpreter {
|
|||||||
fn resolve_expr(&mut self, expr: &Expr) -> Value {
|
fn resolve_expr(&mut self, expr: &Expr) -> Value {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::I64(val) => Value::I64(*val),
|
Expr::I64(val) => Value::I64(*val),
|
||||||
|
Expr::Str(name) => Value::Str(name.clone()),
|
||||||
Expr::BinOp(bo, lhs, rhs) => self.resolve_binop(bo, &lhs, &rhs),
|
Expr::BinOp(bo, lhs, rhs) => self.resolve_binop(bo, &lhs, &rhs),
|
||||||
Expr::UnOp(uo, val) => self.resolve_unop(uo, &val),
|
Expr::UnOp(uo, val) => self.resolve_unop(uo, &val),
|
||||||
Expr::Ident(name) => {
|
Expr::Ident(name) => {
|
||||||
@ -118,7 +123,7 @@ impl Interpreter {
|
|||||||
BinOpType::Le => Value::I64(if lhs <= rhs { 1 } else { 0 }),
|
BinOpType::Le => Value::I64(if lhs <= rhs { 1 } else { 0 }),
|
||||||
BinOpType::Assign => unreachable!(),
|
BinOpType::Assign => unreachable!(),
|
||||||
},
|
},
|
||||||
// _ => panic!("Value types are not compatible"),
|
_ => panic!("Value types are not compatible"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,6 +133,7 @@ impl Interpreter {
|
|||||||
Value::I64(val) => match uo {
|
Value::I64(val) => match uo {
|
||||||
UnOpType::Neg => Value::I64(-val),
|
UnOpType::Neg => Value::I64(-val),
|
||||||
}
|
}
|
||||||
|
_ => panic!("Invalid unary operation for type"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
43
src/lexer.rs
43
src/lexer.rs
@ -7,6 +7,9 @@ pub enum Token {
|
|||||||
/// Integer literal (64-bit)
|
/// Integer literal (64-bit)
|
||||||
I64(i64),
|
I64(i64),
|
||||||
|
|
||||||
|
/// String literal ("Some string")
|
||||||
|
Str(String),
|
||||||
|
|
||||||
/// Left parenthesis ('(')
|
/// Left parenthesis ('(')
|
||||||
LParen,
|
LParen,
|
||||||
|
|
||||||
@ -180,6 +183,46 @@ impl<'a> Lexer<'a> {
|
|||||||
'}' => tokens.push(Token::RBrace),
|
'}' => tokens.push(Token::RBrace),
|
||||||
'$' => tokens.push(Token::Dollar),
|
'$' => tokens.push(Token::Dollar),
|
||||||
|
|
||||||
|
'"' => {
|
||||||
|
let mut text = String::new();
|
||||||
|
|
||||||
|
let mut escape = false;
|
||||||
|
|
||||||
|
// Do as long as a next char exists and it is not '"'
|
||||||
|
loop {
|
||||||
|
if escape {
|
||||||
|
escape = false;
|
||||||
|
|
||||||
|
match self.next() {
|
||||||
|
Some('\\') => text.push('\\'),
|
||||||
|
Some('n') => text.push('\n'),
|
||||||
|
Some('r') => text.push('\r'),
|
||||||
|
Some('t') => text.push('\t'),
|
||||||
|
ch => panic!("Invalid string escape: '{:?}'", ch),
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
match self.peek() {
|
||||||
|
Some('"') => {
|
||||||
|
self.next();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Some('\\') => {
|
||||||
|
self.next();
|
||||||
|
escape = true;
|
||||||
|
}
|
||||||
|
None => panic!("String is never terminated (missing '\"')"),
|
||||||
|
|
||||||
|
_ => text.push(self.next().unwrap()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
tokens.push(Token::Str(text));
|
||||||
|
}
|
||||||
|
|
||||||
'a'..='z' | 'A'..='Z' | '_' => {
|
'a'..='z' | 'A'..='Z' | '_' => {
|
||||||
let mut ident = String::from(ch);
|
let mut ident = String::from(ch);
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::iter::Peekable;
|
use std::{iter::Peekable, rc::Rc};
|
||||||
|
|
||||||
use crate::lexer::Token;
|
use crate::lexer::Token;
|
||||||
|
|
||||||
@ -82,6 +82,8 @@ pub enum Stmt {
|
|||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
/// Integer literal (64-bit)
|
/// Integer literal (64-bit)
|
||||||
I64(i64),
|
I64(i64),
|
||||||
|
/// String literal
|
||||||
|
Str(Rc<String>),
|
||||||
/// Identifier (variable name)
|
/// Identifier (variable name)
|
||||||
Ident(String),
|
Ident(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
|
||||||
@ -93,6 +95,7 @@ pub enum Expr {
|
|||||||
/*
|
/*
|
||||||
## Grammar
|
## Grammar
|
||||||
### Expressions
|
### Expressions
|
||||||
|
LITERAL = I64 | Str
|
||||||
expr_primary = LITERAL | IDENT | "(" expr ")" | "-" expr_primary
|
expr_primary = LITERAL | IDENT | "(" expr ")" | "-" 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)*
|
||||||
@ -265,6 +268,8 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
match self.next() {
|
match self.next() {
|
||||||
Token::I64(val) => Expr::I64(val),
|
Token::I64(val) => Expr::I64(val),
|
||||||
|
|
||||||
|
Token::Str(text) => Expr::Str(text.into()),
|
||||||
|
|
||||||
Token::Ident(name) => Expr::Ident(name),
|
Token::Ident(name) => Expr::Ident(name),
|
||||||
|
|
||||||
Token::LParen => {
|
Token::LParen => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user