statement framework
This commit is contained in:
parent
4cc77ce885
commit
7cc7c5c73c
6 changed files with 163 additions and 33 deletions
|
@ -27,6 +27,14 @@ pub enum Instruction {
|
|||
input: Pointer,
|
||||
dest: Register,
|
||||
},
|
||||
LoadVariable {
|
||||
variable: Pointer,
|
||||
dest: Register,
|
||||
},
|
||||
SetVariable {
|
||||
source: Register,
|
||||
variable: Pointer
|
||||
},
|
||||
Array {
|
||||
dest: Register,
|
||||
},
|
||||
|
@ -45,6 +53,7 @@ pub enum Instruction {
|
|||
#[derive(Debug)]
|
||||
pub enum BinaryOperation {
|
||||
Add, Subtract, Multiply, Divide,
|
||||
Equals,
|
||||
In,
|
||||
}
|
||||
|
||||
|
@ -65,14 +74,17 @@ fn get_binary_type(operation: &BinaryOperation, data_type_left: &ValueType, data
|
|||
(Add | Subtract | Multiply | Divide, ValueType::Float, ValueType::Float) => ValueType::Float,
|
||||
(In, item, ValueType::Array(generic)) if item == generic.as_ref() => ValueType::Boolean,
|
||||
(Add, ValueType::String, ValueType::String) => ValueType::String,
|
||||
(Equals, left, right) if left == right => ValueType::Boolean,
|
||||
_ => panic!("invalid types"),
|
||||
}
|
||||
}
|
||||
|
||||
use parser::Expr;
|
||||
|
||||
impl Compiler {
|
||||
fn tree(&mut self, tree: &parser::Tree) -> (ValueType, Register) {
|
||||
fn tree(&mut self, tree: &Expr) -> (ValueType, Register) {
|
||||
match tree {
|
||||
parser::Tree::Value { term, kind } => {
|
||||
Expr::Value { term, kind } => {
|
||||
use crate::lexer::TokenType;
|
||||
let (data_type, data) = match kind {
|
||||
TokenType::Float => {
|
||||
|
@ -82,6 +94,15 @@ impl Compiler {
|
|||
TokenType::String => {
|
||||
(ValueType::String, Value::String(term[1..term.len() - 1].to_string()))
|
||||
},
|
||||
TokenType::Ident => {
|
||||
let reg = self.next_register();
|
||||
let idx = self.push_data(Value::String(term.clone()));
|
||||
self.code.push(Instruction::LoadVariable {
|
||||
variable: idx,
|
||||
dest: reg,
|
||||
});
|
||||
return (ValueType::Float, reg);
|
||||
},
|
||||
_ => todo!(),
|
||||
};
|
||||
let reg = self.next_register();
|
||||
|
@ -92,7 +113,7 @@ impl Compiler {
|
|||
});
|
||||
(data_type, reg)
|
||||
},
|
||||
parser::Tree::Unary { op, term } => {
|
||||
Expr::Unary { op, term } => {
|
||||
let (data_type, reg_input) = self.tree(term);
|
||||
let reg_dest = self.next_register();
|
||||
let operator = match op.as_str() {
|
||||
|
@ -106,7 +127,7 @@ impl Compiler {
|
|||
});
|
||||
(data_type, reg_dest)
|
||||
},
|
||||
parser::Tree::Binary { op, left, right } => {
|
||||
Expr::Binary { op, left, right } => {
|
||||
let (data_type_left, reg_left) = self.tree(left);
|
||||
let (data_type_right, reg_right) = self.tree(right);
|
||||
let reg_dest = self.next_register();
|
||||
|
@ -115,6 +136,7 @@ impl Compiler {
|
|||
"-" => BinaryOperation::Subtract,
|
||||
"*" => BinaryOperation::Multiply,
|
||||
"/" => BinaryOperation::Divide,
|
||||
"==" => BinaryOperation::Equals,
|
||||
"in" => BinaryOperation::In,
|
||||
_ => todo!(),
|
||||
};
|
||||
|
@ -127,7 +149,20 @@ impl Compiler {
|
|||
});
|
||||
(data_type, reg_dest)
|
||||
},
|
||||
parser::Tree::Array { terms } => {
|
||||
// Tree::Ternary { op, left, middle, right } => {
|
||||
// let (data_type_left, reg_left) = self.tree(left);
|
||||
// let (data_type_middle, reg_middle) = self.tree(middle);
|
||||
// let (data_type_right, reg_right) = self.tree(right);
|
||||
// if data_type_left != ValueType::Boolean {
|
||||
// panic!("different data types");
|
||||
// }
|
||||
// if data_type_middle != data_type_right {
|
||||
// panic!("different data types");
|
||||
// }
|
||||
// // reg_left, reg_right
|
||||
// (ValueType::Boolean, 0)
|
||||
// },
|
||||
Expr::Array { terms } => {
|
||||
let mut terms = terms.iter();
|
||||
let (kind, first_reg) = self.tree(terms.next().expect("needs at least 1 item for now"));
|
||||
let mut array = vec![self.data[first_reg as usize].clone()];
|
||||
|
@ -169,7 +204,7 @@ impl Compiler {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn expr(tree: &parser::Tree) -> Bytecode {
|
||||
pub fn expr(tree: &parser::Expr) -> Bytecode {
|
||||
let mut compiler = Compiler {
|
||||
code: Vec::new(),
|
||||
data: Vec::new(),
|
||||
|
|
38
src/main.rs
38
src/main.rs
|
@ -13,6 +13,8 @@ mod types;
|
|||
use types::Value;
|
||||
use parser::Kind;
|
||||
|
||||
use crate::runner::Runtime;
|
||||
|
||||
fn test(code: &str) -> Value {
|
||||
let tokens = lexer::lex(code);
|
||||
let tokens: Vec<_> = tokens
|
||||
|
@ -22,7 +24,9 @@ fn test(code: &str) -> Value {
|
|||
let tree = parser::expr(&mut tokens.iter().peekable(), 0);
|
||||
let bytecode = compiler::expr(&tree);
|
||||
dbg!(&bytecode);
|
||||
let result = runner::execute(&bytecode);
|
||||
let mut runtime = Runtime::new();
|
||||
runtime.global.insert("foo".to_owned(), Value::Float(3.5));
|
||||
let result = runner::execute(&bytecode, &mut runtime);
|
||||
dbg!(&result);
|
||||
result
|
||||
}
|
||||
|
@ -39,14 +43,6 @@ fn parse_kind(code: &str) -> Kind {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
// test("math.sin");
|
||||
// test("math.sin(10 * math.pi)");
|
||||
test("\"foo\" == \"foo\" ? 1 : 4");
|
||||
test("false ? 3 : 4");
|
||||
// test("\"hello\".length");
|
||||
// test("[1, 2, 3].length");
|
||||
// test("str.capitalize(\"hello world\")");
|
||||
// test("\"apple\" in str.split(\"apple banana orange\") ? \"apple exists\" : \"apple doesn\'t exist\"");
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -145,4 +141,28 @@ mod tests {
|
|||
]));
|
||||
assert_eq!(parse_kind("struct { foo: u8, bar: u8, baz: struct { one: u8, two: u8 } }"), k);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variable() {
|
||||
assert_eq!(test("foo"), Value::Float(3.5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variable_in_expr() {
|
||||
assert_eq!(test("(foo + 5) * 4"), Value::Float(34.0));
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn test_ternary() {
|
||||
// assert_eq!(test("false ? 3 : 4"), Value::Float(4.0));
|
||||
// }
|
||||
|
||||
// test("math.sin");
|
||||
// test("math.sin(10 * math.pi)");
|
||||
// test("\"foo\" == \"foo\" ? 1 : 4");
|
||||
// test("\"hello\".length");
|
||||
// test("[1, 2, 3].length");
|
||||
// test("str.capitalize(\"hello world\")");
|
||||
// test("\"apple\" in str.split(\"apple banana orange\") ? \"apple exists\" : \"apple doesn\'t exist\"");
|
||||
|
||||
}
|
||||
|
|
|
@ -6,13 +6,13 @@ pub enum ParserError {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Tree {
|
||||
Unary { op: String, term: Box<Tree> },
|
||||
Binary { op: String, left: Box<Tree>, right: Box<Tree> },
|
||||
Ternary { op: String, left: Box<Tree>, middle: Box<Tree>, right: Box<Tree> },
|
||||
Function { ident: Box<Tree>, terms: Vec<Tree> },
|
||||
Index { array: Box<Tree>, index: Box<Tree> },
|
||||
Array { terms: Vec<Tree> },
|
||||
pub enum Expr {
|
||||
Unary { op: String, term: Box<Expr> },
|
||||
Binary { op: String, left: Box<Expr>, right: Box<Expr> },
|
||||
Ternary { op: String, left: Box<Expr>, middle: Box<Expr>, right: Box<Expr> },
|
||||
Function { ident: Box<Expr>, terms: Vec<Expr> },
|
||||
Index { array: Box<Expr>, index: Box<Expr> },
|
||||
Array { terms: Vec<Expr> },
|
||||
Value { term: String, kind: lexer::TokenType },
|
||||
}
|
||||
|
||||
|
@ -83,9 +83,9 @@ impl TryFrom<&str> for TernaryOpType {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parse(tokens: &mut std::iter::Peekable<std::slice::Iter<&(lexer::TokenType, &str)>>, min_bp: usize) -> Tree {
|
||||
pub fn parse(tokens: &mut std::iter::Peekable<std::slice::Iter<&(lexer::TokenType, &str)>>, min_bp: usize) -> Expr {
|
||||
let mut lhs = match tokens.next().expect("missing token") {
|
||||
token @ (_, "-") | token @ (_, "!") => Tree::Unary { op: token.1.to_owned(), term: Box::new(parse(tokens, 19)) },
|
||||
token @ (_, "-") | token @ (_, "!") => Expr::Unary { op: token.1.to_owned(), term: Box::new(parse(tokens, 19)) },
|
||||
(_, "(") => {
|
||||
if tokens.peek().map(|i| i.1) == Some(")") {
|
||||
panic!("empty paranthases");
|
||||
|
@ -114,11 +114,11 @@ pub fn parse(tokens: &mut std::iter::Peekable<std::slice::Iter<&(lexer::TokenTyp
|
|||
panic!("missing closing bracket");
|
||||
}
|
||||
tokens.next();
|
||||
Tree::Array { terms: cons }
|
||||
Expr::Array { terms: cons }
|
||||
},
|
||||
token @ (lexer::TokenType::Symbol, _) => panic!("unexpected token {}", token.1),
|
||||
token @ (_, "in") => panic!("unexpected token {}", token.1),
|
||||
token @ (kind, _) => Tree::Value { term: token.1.to_owned(), kind: *kind },
|
||||
token @ (kind, _) => Expr::Value { term: token.1.to_owned(), kind: *kind },
|
||||
};
|
||||
|
||||
loop {
|
||||
|
@ -135,7 +135,7 @@ pub fn parse(tokens: &mut std::iter::Peekable<std::slice::Iter<&(lexer::TokenTyp
|
|||
t @ "(" | t @ "[" => {
|
||||
tokens.next();
|
||||
if t == "(" {
|
||||
let mut cons: Vec<Tree> = Vec::new();
|
||||
let mut cons: Vec<Expr> = Vec::new();
|
||||
while tokens.peek().map(|i| i.1) != Some(")") {
|
||||
cons.push(parse(tokens, 0));
|
||||
if tokens.peek().map(|i| i.1) == Some(",") {
|
||||
|
@ -145,18 +145,18 @@ pub fn parse(tokens: &mut std::iter::Peekable<std::slice::Iter<&(lexer::TokenTyp
|
|||
}
|
||||
}
|
||||
if tokens.peek().map(|i| i.1) != Some(")") { panic!("missing a closing paranthase") }
|
||||
lhs = Tree::Function { ident: Box::new(lhs), terms: cons };
|
||||
lhs = Expr::Function { ident: Box::new(lhs), terms: cons };
|
||||
} else {
|
||||
let index = parse(tokens, 0);
|
||||
if tokens.peek().map(|i| i.1) != Some("]") { panic!("missing a closing bracket") }
|
||||
lhs = Tree::Index { array: Box::new(lhs), index: Box::new(index) };
|
||||
lhs = Expr::Index { array: Box::new(lhs), index: Box::new(index) };
|
||||
}
|
||||
},
|
||||
"?" => {
|
||||
tokens.next();
|
||||
let middle = parse(tokens, 0);
|
||||
if tokens.next().map(|i| i.1) != Some(":") { panic!("missing a colon") }
|
||||
lhs = Tree::Ternary {
|
||||
lhs = Expr::Ternary {
|
||||
op: "?".into(),
|
||||
left: Box::new(lhs),
|
||||
middle: Box::new(middle),
|
||||
|
@ -165,7 +165,7 @@ pub fn parse(tokens: &mut std::iter::Peekable<std::slice::Iter<&(lexer::TokenTyp
|
|||
},
|
||||
symbol => {
|
||||
tokens.next();
|
||||
lhs = Tree::Binary {
|
||||
lhs = Expr::Binary {
|
||||
op: symbol.to_owned(),
|
||||
left: Box::new(lhs),
|
||||
right: Box::new(parse(tokens, right_bp)),
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
mod expr;
|
||||
mod kind;
|
||||
mod statement;
|
||||
|
||||
pub use expr::{
|
||||
UnaryOpType, BinaryOpType, TernaryOpType,
|
||||
parse as expr, Tree, ParserError,
|
||||
parse as expr, Expr, ParserError,
|
||||
};
|
||||
|
||||
pub use kind::{
|
||||
parse as kind, Kind,
|
||||
};
|
||||
|
||||
pub use statement::{
|
||||
parse as statement, Statement,
|
||||
};
|
||||
|
|
44
src/parser/statement.rs
Normal file
44
src/parser/statement.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
use crate::lexer::{self, TokenType};
|
||||
use super::{expr, Expr};
|
||||
|
||||
pub enum Statement {
|
||||
Expression(Box<Expr>),
|
||||
Let(String, Box<Expr>),
|
||||
Block(Vec<Statement>),
|
||||
None,
|
||||
}
|
||||
|
||||
pub fn parse(tokens: &mut std::iter::Peekable<std::slice::Iter<&(lexer::TokenType, &str)>>) -> Statement {
|
||||
match tokens.peek() {
|
||||
Some((_, "{")) => {
|
||||
tokens.next();
|
||||
let mut statements = Vec::new();
|
||||
while let Some(next) = tokens.peek() {
|
||||
if matches!(next, (_, "}")) {
|
||||
break;
|
||||
}
|
||||
tokens.next();
|
||||
statements.push(parse(tokens));
|
||||
if !matches!(tokens.peek(), Some((_, ";"))) {
|
||||
break;
|
||||
}
|
||||
tokens.next();
|
||||
}
|
||||
if !matches!(tokens.next(), Some((_, "}"))) {
|
||||
panic!("missing }}");
|
||||
}
|
||||
Statement::Block(statements)
|
||||
},
|
||||
Some((lexer::TokenType::Keyword, "let")) => {
|
||||
tokens.next();
|
||||
let Some((TokenType::Ident, var)) = tokens.next() else {
|
||||
panic!("expected variable name");
|
||||
};
|
||||
assert!(matches!(tokens.next(), Some((_, "="))));
|
||||
let ex = Box::new(expr(tokens, 0));
|
||||
Statement::Let(var.to_string(), ex)
|
||||
},
|
||||
Some(_) => Statement::Expression(Box::new(expr(tokens, 0))),
|
||||
None => panic!("no more"),
|
||||
}
|
||||
}
|
|
@ -4,8 +4,23 @@ use crate::types;
|
|||
use crate::compiler;
|
||||
|
||||
use types::Value;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn execute(bytecode: &compiler::Bytecode) -> Value {
|
||||
pub struct Runtime {
|
||||
pub global: HashMap<String, Value>,
|
||||
pub variables: HashMap<String, Value>,
|
||||
}
|
||||
|
||||
impl Runtime {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
global: HashMap::new(),
|
||||
variables: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(bytecode: &compiler::Bytecode, runtime: &mut Runtime) -> Value {
|
||||
// let mut data: [f64; 256] = [0.0; 256];
|
||||
let mut data: [Value; 8] = [0; 8].map(|_| Value::Float(0.0));
|
||||
for instruction in &bytecode.code {
|
||||
|
@ -59,6 +74,17 @@ pub fn execute(bytecode: &compiler::Bytecode) -> Value {
|
|||
let Value::Array { data: ref mut arr, .. } = data[*dest as usize] else { panic!("not an array") };
|
||||
arr.push(item);
|
||||
},
|
||||
LoadVariable { variable, dest } => {
|
||||
let Value::String(ref s) = bytecode.data[*variable as usize] else { panic!("not a string") };
|
||||
data[*dest as usize] = runtime.variables.get(s.as_str())
|
||||
.or_else(|| runtime.global.get(s.as_str()))
|
||||
.expect("variable does not exist")
|
||||
.clone();
|
||||
},
|
||||
SetVariable { variable, source } => {
|
||||
let Value::String(ref s) = bytecode.data[*variable as usize] else { panic!("not a string") };
|
||||
runtime.variables.insert(s.to_string(), bytecode.data[*variable as usize].clone());
|
||||
},
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue