diff --git a/Cargo.toml b/Cargo.toml index e1afac3..bc65b6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "oxido" -version = "2.1.2" +version = "2.2.2" edition = "2021" [profile.release] @@ -13,7 +13,4 @@ debug = true [dependencies] clap = { version = "3.1.18", features = ["derive"] } -codespan-reporting = "0.11.1" -inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = [ - "llvm14-0", -] } +codespan-reporting = "0.11.1" \ No newline at end of file diff --git a/README.md b/README.md index 9071219..b2b3456 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Conventionally, Oxido files are named `main.oxi`. * String: A string is any value inside `"` (double quotes) passing the regex `\"[A-Za-z0-9 !]+\"`. -* Integer: Integers (no fractions), passing the regex `[0-9]+`. +* Int: Integers (no fractions), passing the regex `[0-9]+`. * Bool: `true` or `false` @@ -88,7 +88,7 @@ let a: int = 0; a = 5; a = 5 * 5; a = factorial(5); -a = "Hi mom!"; // errors +a = "Hi mom!"; // error: incorrect data type ``` ### If statements @@ -139,9 +139,9 @@ message(text); The `exit` keyword can be used to exit the program with the specified exit code ```rs -print("Hi mom!") +print("Hi mom!"); -exit +exit(0); ``` ## Standard Library diff --git a/src/ast.rs b/src/ast.rs index 2bbfe41..6fb2350 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,24 +1,26 @@ use std::ops::Range; -use crate::token::Token; +use crate::{token::Token, data::{DataType, Param}}; + +pub type Ast = Vec<(AstNode, Range)>; #[derive(Clone, Debug)] pub enum AstNode { - Assignment(String, Expression), - ReAssignment(String, Expression), - If(Expression, Vec<(AstNode, Range)>), - Loop(Vec<(AstNode, Range)>), + Assignment(String, DataType, Expression), + ReAssignment(String, DataType, Expression), + If(Expression, Ast), + Loop(Ast), FunctionCall(String, Vec), - FunctionDeclaration(String, Vec, Vec<(AstNode, Range)>), + FunctionDeclaration(String, Vec, Ast), Break, Return(Expression), - Exit + Exit(Expression), } #[derive(Clone, Debug)] pub enum Expression { BinaryOperation(Box, Token, Box), Str(String), - Integer(i64), + Int(i64), Bool(bool), FunctionCall(String, Vec), Identifier(String) diff --git a/src/data.rs b/src/data.rs new file mode 100644 index 0000000..70a1323 --- /dev/null +++ b/src/data.rs @@ -0,0 +1,90 @@ +use std::fmt::{self, Display}; + +use crate::ast::Ast; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum Data { + Str(String), + Int(i64), + Bool(bool), +} + +impl Data { + pub fn to_string(&self) -> &str { + match self { + Data::Str(_) => "str", + Data::Int(_) => "int", + Data::Bool(_) => "bool", + } + } + + pub fn r#type(&self) -> DataType { + match self { + Data::Str(_) => DataType::Str, + Data::Int(_) => DataType::Int, + Data::Bool(_) => DataType::Bool, + } + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum DataType { + Str, + Int, + Bool, +} + +impl Display for DataType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + String::from(match self { + DataType::Str => "str", + DataType::Int => "int", + DataType::Bool => "bool", + }) + ) + } +} + +#[derive(Clone, Debug)] +pub struct Variable { + pub datatype: DataType, + pub data: Data, +} + +impl Variable { + pub fn new(datatype: DataType, data: Data) -> Self { + Self { datatype, data } + } +} + +#[derive(Clone, Debug)] +pub struct Param { + pub name: String, + pub datatype: DataType, +} + +impl Param { + pub fn new(name: String, datatype: DataType) -> Self { + Self { name, datatype } + } +} + +#[derive(Clone, Debug)] +pub struct Function { + pub name: String, + pub params: Vec, + pub statements: Ast, +} + +impl Function { + pub fn new(name: String, params: Vec, statements: Ast) -> Self { + Self { + name, + params, + statements, + } + } +} diff --git a/src/datatype.rs b/src/datatype.rs deleted file mode 100644 index d8e6862..0000000 --- a/src/datatype.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::ops::Range; - -use crate::ast::AstNode; - -#[derive(Clone, Debug)] -pub enum Data { - Str(String), - Integer(i64), - Bool(bool), -} - -impl Data { - pub fn type_as_str(&self) -> &str { - match self { - Data::Str(_) => "Str", - Data::Integer(_) => "Integer", - Data::Bool(_) => "Bool", - } - } -} - -#[derive(Clone, Debug)] -pub struct Function { - pub name: String, - pub params: Vec, - pub statements: Vec<(AstNode, Range)>, -} - -impl Function { - pub fn new(name: String, params: Vec, statements: Vec<(AstNode, Range)>) -> Self { - Self { - name, - params, - statements, - } - } -} diff --git a/src/interpreter.rs b/src/interpreter.rs index 10d363b..6aec17e 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,8 +1,8 @@ -use std::{collections::HashMap, process, ops::Range}; +use std::{collections::HashMap, ops::Range, process}; use crate::{ - ast::{AstNode, Expression}, - datatype::{Data, Function}, + ast::{Ast, AstNode, Expression}, + data::{Data, Function, Variable}, error::error, standardlibrary::StandardLibrary, token::Token, @@ -13,7 +13,7 @@ pub struct Interpreter<'a> { file: &'a str, stop: bool, returned: Option, - variables: HashMap, + variables: HashMap, functions: HashMap, } @@ -29,7 +29,7 @@ impl<'a> Interpreter<'a> { } } - pub fn run(&mut self, ast: Vec<(AstNode, Range)>) { + pub fn run(&mut self, ast: Ast) { let mut stream = ast.into_iter().peekable(); loop { if stream.peek().is_none() { @@ -45,12 +45,12 @@ impl<'a> Interpreter<'a> { return; } match node.0 { - AstNode::Assignment(ident, expression) => { + AstNode::Assignment(ident, datatype, expression) => { let data = self.parse_expression(expression, &node.1); - self.variables.insert(ident, data); + self.variables.insert(ident, Variable::new(datatype, data)); } - AstNode::ReAssignment(ident, expression) => { - if !self.variables.contains_key(&*ident) { + AstNode::ReAssignment(ident, datatype, expression) => { + if !self.variables.contains_key(&ident) { error( self.name, self.file, @@ -61,7 +61,7 @@ impl<'a> Interpreter<'a> { ); } let data = self.parse_expression(expression, &node.1); - self.variables.insert(ident, data); + self.variables.insert(ident, Variable::new(datatype, data)); } AstNode::If(condition, statements) => { let data = self.parse_expression(condition, &node.1); @@ -84,7 +84,7 @@ impl<'a> Interpreter<'a> { "0002", &format!( "mismatched data types, expected `bool` found {}", - data.type_as_str() + data.to_string() ), "a value of type `bool` was expected", &node.1, @@ -140,7 +140,25 @@ impl<'a> Interpreter<'a> { for (i, arg) in args.iter().enumerate() { let param = function.params.get(i).unwrap(); - self.variables.insert(param.to_string(), arg.to_owned()); + if param.datatype != arg.r#type() { + error( + self.name, + self.file, + "E00011", + "incorrect data type", + &format!( + "mismatched data types expected {} found {}", + param.datatype, + arg.to_string() + ), + &node.1, + ) + } + + self.variables.insert( + param.name.clone(), + Variable::new(param.datatype, arg.to_owned()), + ); } let mut stream = function.statements.into_iter().peekable(); @@ -176,7 +194,26 @@ impl<'a> Interpreter<'a> { self.stop = true; } AstNode::Return(expr) => self.returned = Some(self.parse_expression(expr, &node.1)), - AstNode::Exit => process::exit(0), + AstNode::Exit(expr) => { + let data = self.parse_expression(expr, &node.1); + + match data { + Data::Int(n) => process::exit(n.try_into().unwrap()), + _ => { + error( + self.name, + self.file, + "0002", + &format!( + "mismatched data types, expected `int` found {}", + data.to_string() + ), + "a value of type `String` was expected", + &node.1, + ); + } + }; + } } } @@ -221,7 +258,23 @@ impl<'a> Interpreter<'a> { let data = self.parse_expression(arg.to_owned(), pos); - self.variables.insert(param.to_string(), data); + if param.datatype != data.r#type() { + error( + self.name, + self.file, + "E00011", + &format!( + "mismatched data types expected {} found {}", + param.datatype, + data.to_string() + ), + "incorrect data type", + pos, + ) + } + + self.variables + .insert(param.name.clone(), Variable::new(param.datatype, data)); } let mut stream = function.statements.into_iter().peekable(); @@ -252,13 +305,11 @@ impl<'a> Interpreter<'a> { Expression::BinaryOperation(lhs, op, rhs) => { self.parse_binary_operation(*lhs, op, *rhs, pos) } - Expression::Integer(i) => Data::Integer(i), - Expression::Identifier(i) => self.variables.get(&*i).unwrap().to_owned(), + Expression::Int(i) => Data::Int(i), + Expression::Identifier(i) => self.variables.get(&*i).unwrap().to_owned().data, Expression::Bool(b) => Data::Bool(b), Expression::Str(s) => Data::Str(s), - Expression::FunctionCall(f, args) => { - self.parse_function(f, args, pos) - } + Expression::FunctionCall(f, args) => self.parse_function(f, args, pos), } } @@ -269,43 +320,9 @@ impl<'a> Interpreter<'a> { rhs: Expression, pos: &Range, ) -> Data { - let lhs = match lhs { - Expression::BinaryOperation(lhs, op, rhs) => { - self.parse_binary_operation(*lhs, op, *rhs, pos) - } - Expression::Integer(i) => Data::Integer(i), - Expression::Identifier(i) => { - if !self.variables.contains_key(&*i) { - error( - self.name, - self.file, - "0006", - "attempt to access value of undeclared variable", - "declare the value of the variable before using it", - pos, - ); - }; - self.variables.get(&*i).unwrap().to_owned() - } - Expression::Str(s) => Data::Str(s), - Expression::Bool(b) => Data::Bool(b), - Expression::FunctionCall(f, args) => { - self.parse_function(f, args, pos) - } - }; + let lhs = self.parse_expression(lhs, pos); let operator = op; - let rhs = match rhs { - Expression::BinaryOperation(lhs, op, rhs) => { - self.parse_binary_operation(*lhs, op, *rhs, pos) - } - Expression::Integer(i) => Data::Integer(i), - Expression::Identifier(i) => self.variables.get(&*i).unwrap().to_owned(), - Expression::Str(s) => Data::Str(s), - Expression::Bool(b) => Data::Bool(b), - Expression::FunctionCall(f, args) => { - self.parse_function(f, args, pos) - } - }; + let rhs = self.parse_expression(rhs, pos); match operator { Token::Addition => match lhs { Data::Str(str) => match rhs { @@ -316,21 +333,21 @@ impl<'a> Interpreter<'a> { "0002", &format!( "mismatched data types, expected `String` found {}", - data.type_as_str() + data.to_string() ), "a value of type `String` was expected", pos, ), }, - Data::Integer(n) => match rhs { - Data::Integer(m) => Data::Integer(n + m), + Data::Int(n) => match rhs { + Data::Int(m) => Data::Int(n + m), data => error( self.name, self.file, "0002", &format!( "mismatched data types, expected `int` found {}", - data.type_as_str() + data.to_string() ), "a value of type `int` was expected", pos, @@ -342,22 +359,22 @@ impl<'a> Interpreter<'a> { "0002", &format!( "mismatched data types, expected `String` or `int` found {}", - data.type_as_str() + data.to_string() ), "a value of type `String` or `int` was expected", pos, ), }, Token::Subtraction => match lhs { - Data::Integer(n) => match rhs { - Data::Integer(m) => Data::Integer(n - m), + Data::Int(n) => match rhs { + Data::Int(m) => Data::Int(n - m), data => error( self.name, self.file, "0002", &format!( "mismatched data types, expected `int` found {}", - data.type_as_str() + data.to_string() ), "a value of type `int` was expected", pos, @@ -369,22 +386,22 @@ impl<'a> Interpreter<'a> { "0002", &format!( "mismatched data types, expected `int` found {}", - data.type_as_str() + data.to_string() ), "a value of type `int` was expected", pos, ), }, Token::Multiplication => match lhs { - Data::Integer(n) => match rhs { - Data::Integer(m) => Data::Integer(n * m), + Data::Int(n) => match rhs { + Data::Int(m) => Data::Int(n * m), data => error( self.name, self.file, "0002", &format!( "mismatched data types, expected `int` found {}", - data.type_as_str() + data.to_string() ), "a value of type `int` was expected", pos, @@ -396,22 +413,22 @@ impl<'a> Interpreter<'a> { "0002", &format!( "mismatched data types, expected `int` found {}", - data.type_as_str() + data.to_string() ), "a value of type `int` was expected", pos, ), }, Token::Division => match lhs { - Data::Integer(n) => match rhs { - Data::Integer(m) => Data::Integer(n / m), + Data::Int(n) => match rhs { + Data::Int(m) => Data::Int(n / m), data => error( self.name, self.file, "0002", &format!( "mismatched data types, expected `int` found {}", - data.type_as_str() + data.to_string() ), "a value of type `int` was expected", pos, @@ -423,22 +440,22 @@ impl<'a> Interpreter<'a> { "0002", &format!( "mismatched data types, expected `int` found {}", - data.type_as_str() + data.to_string() ), "a value of type `int` was expected", pos, ), }, Token::Power => match lhs { - Data::Integer(n) => match rhs { - Data::Integer(m) => Data::Integer(n.pow(m as u32)), + Data::Int(n) => match rhs { + Data::Int(m) => Data::Int(n.pow(m as u32)), data => error( self.name, self.file, "0002", &format!( "mismatched data types, expected `int` found {}", - data.type_as_str() + data.to_string() ), "a value of type `int` was expected", pos, @@ -450,7 +467,7 @@ impl<'a> Interpreter<'a> { "0002", &format!( "mismatched data types, expected `int` found {}", - data.type_as_str() + data.to_string() ), "a value of type `int` was expected", pos, @@ -465,21 +482,21 @@ impl<'a> Interpreter<'a> { "0002", &format!( "mismatched data types, expected `String` found {}", - data.type_as_str() + data.to_string() ), "a value of type `String` was expected", pos, ), }, - Data::Integer(n) => match rhs { - Data::Integer(m) => Data::Bool(n == m), + Data::Int(n) => match rhs { + Data::Int(m) => Data::Bool(n == m), data => error( self.name, self.file, "0002", &format!( "mismatched data types, expected `int` found {}", - data.type_as_str() + data.to_string() ), "a value of type `int` was expected", pos, @@ -493,7 +510,7 @@ impl<'a> Interpreter<'a> { "0002", &format!( "mismatched data types, expected `bool` found {}", - data.type_as_str() + data.to_string() ), "a value of type `bool` was expected", pos, @@ -509,21 +526,21 @@ impl<'a> Interpreter<'a> { "0002", &format!( "mismatched data types, expected `String` found {}", - data.type_as_str() + data.to_string() ), "a value of type `String` was expected", pos, ), }, - Data::Integer(n) => match rhs { - Data::Integer(m) => Data::Bool(n != m), + Data::Int(n) => match rhs { + Data::Int(m) => Data::Bool(n != m), data => error( self.name, self.file, "0002", &format!( "mismatched data types, expected `int` found {}", - data.type_as_str() + data.to_string() ), "a value of type `int` was expected", pos, @@ -537,7 +554,7 @@ impl<'a> Interpreter<'a> { "0002", &format!( "mismatched data types, expected `bool` found {}", - data.type_as_str() + data.to_string() ), "a value of type `bool` was expected", pos, @@ -553,21 +570,21 @@ impl<'a> Interpreter<'a> { "0002", &format!( "mismatched data types, expected `String` found {}", - data.type_as_str() + data.to_string() ), "a value of type `String` was expected", pos, ), }, - Data::Integer(n) => match rhs { - Data::Integer(m) => Data::Bool(n > m), + Data::Int(n) => match rhs { + Data::Int(m) => Data::Bool(n > m), data => error( self.name, self.file, "0002", &format!( "mismatched data types, expected `int` found {}", - data.type_as_str() + data.to_string() ), "a value of type `int` was expected", pos, @@ -581,7 +598,7 @@ impl<'a> Interpreter<'a> { "0002", &format!( "mismatched data types, expected `bool` found {}", - data.type_as_str() + data.to_string() ), "a value of type `bool` was expected", pos, @@ -597,21 +614,21 @@ impl<'a> Interpreter<'a> { "0002", &format!( "mismatched data types, expected `String` found {}", - data.type_as_str() + data.to_string() ), "a value of type `String` was expected", pos, ), }, - Data::Integer(n) => match rhs { - Data::Integer(m) => Data::Bool(n < m), + Data::Int(n) => match rhs { + Data::Int(m) => Data::Bool(n < m), data => error( self.name, self.file, "0002", &format!( "mismatched data types, expected `int` found {}", - data.type_as_str() + data.to_string() ), "a value of type `int` was expected", pos, @@ -625,7 +642,7 @@ impl<'a> Interpreter<'a> { "0002", &format!( "mismatched data types, expected `bool` found {}", - data.type_as_str() + data.to_string() ), "a value of type `bool` was expected", pos, @@ -641,21 +658,21 @@ impl<'a> Interpreter<'a> { "0002", &format!( "mismatched data types, expected `String` found {}", - data.type_as_str() + data.to_string() ), "a value of type `String` was expected", pos, ), }, - Data::Integer(n) => match rhs { - Data::Integer(m) => Data::Bool(n >= m), + Data::Int(n) => match rhs { + Data::Int(m) => Data::Bool(n >= m), data => error( self.name, self.file, "0002", &format!( "mismatched data types, expected `int` found {}", - data.type_as_str() + data.to_string() ), "a value of type `int` was expected", pos, @@ -669,7 +686,7 @@ impl<'a> Interpreter<'a> { "0002", &format!( "mismatched data types, expected `bool` found {}", - data.type_as_str() + data.to_string() ), "a value of type `bool` was expected", pos, @@ -685,21 +702,21 @@ impl<'a> Interpreter<'a> { "0002", &format!( "mismatched data types, expected `String` found {}", - data.type_as_str() + data.to_string() ), "a value of type `String` was expected", pos, ), }, - Data::Integer(n) => match rhs { - Data::Integer(m) => Data::Bool(n <= m), + Data::Int(n) => match rhs { + Data::Int(m) => Data::Bool(n <= m), data => error( self.name, self.file, "0002", &format!( "mismatched data types, expected `int` found {}", - data.type_as_str() + data.to_string() ), "a value of type `int` was expected", pos, @@ -713,7 +730,7 @@ impl<'a> Interpreter<'a> { "0002", &format!( "mismatched data types, expected `bool` found {}", - data.type_as_str() + data.to_string() ), "a value of type `bool` was expected", pos, diff --git a/src/lexer.rs b/src/lexer.rs index 688fa74..b30c72a 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -1,4 +1,4 @@ -use crate::{error::error, token::Token}; +use crate::{error::error, token::{Token, Tokens}, data::DataType}; pub struct Lexer<'a> { name: &'a str, @@ -17,7 +17,7 @@ impl<'a> Lexer<'a> { } } - pub fn run(&mut self) -> Option<&Vec<(Token, usize)>> { + pub fn run(&mut self) -> Option<&Tokens> { let file = self.file.split("").collect::>(); let mut stream = file.iter().peekable(); @@ -136,9 +136,61 @@ impl<'a> Lexer<'a> { token.push(ch); } - let t = Token::Integer(token.parse::().unwrap()); + let t = Token::Int(token.parse::().unwrap()); let size = t.len(); self.tokens.push((t, self.at - size)); + continue; + } else if ch == ':' { + loop { + let ch = stream.peek(); + self.at += 1; + + if ch.is_none() { + if self.at > size { + break; + } + stream.next(); + continue; + } + + if ch?.chars().next()?.is_whitespace() { + stream.next(); + continue; + } + + if !ch?.chars().next()?.is_alphabetic() { + self.at -= 1; + break; + } + + let ch = if ch?.is_empty() { + stream.next(); + continue; + } else { + stream.next()?.chars().next()? + }; + + token.push(ch); + } + + let t = match token.as_str() { + "str" => Token::DataType(DataType::Str), + "int" => Token::DataType(DataType::Int), + "bool" => Token::DataType(DataType::Bool), + _ => error( + self.name, + self.file, + "0001", + &format!("expected datatype found `{ch}`"), + &format!("token `{ch}` was not expected here"), + &(self.at..self.at + 1), + ) + }; + + let size = t.len(); + + self.tokens.push((t, self.at - size)); + continue; } else if ['+', '-', '*', '/', '^', '!', '=', '>', '<'].contains(&ch) { let t = match ch { @@ -295,9 +347,7 @@ impl<'a> Lexer<'a> { "true" => Token::Bool(true), "false" => Token::Bool(false), _ => { - if self.tokens.last().is_some() - && self.tokens.last()?.0 == Token::Fn - { + if self.tokens.last().is_some() && self.tokens.last()?.0 == Token::Fn { Token::FunctionName(token) } else { let mut cloned_stream = stream.clone(); diff --git a/src/main.rs b/src/main.rs index 5c95497..45e64e0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ use std::process::exit; use std::time::Instant; mod ast; -mod datatype; +mod data; mod error; mod interpreter; mod lexer; diff --git a/src/parser.rs b/src/parser.rs index 7823c8d..dd968c9 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,9 +1,10 @@ use std::{iter::Peekable, ops::Range, vec::IntoIter}; use crate::{ - ast::{AstNode, Expression}, + ast::{Ast, AstNode, Expression}, + data::Param, error::error, - token::Token, + token::{Token, Tokens}, }; pub struct Parser<'a> { @@ -16,18 +17,15 @@ impl<'a> Parser<'a> { Self { name, file } } - pub fn run(&'a self, tokens: Vec<(Token, usize)>) -> Option)>> { + pub fn run(&'a self, tokens: Tokens) -> Option { let ast = self.match_tokens(tokens)?; Some(ast) } - pub fn match_tokens( - &'a self, - tokens: Vec<(Token, usize)>, - ) -> Option)>> { + pub fn match_tokens(&'a self, tokens: Tokens) -> Option { let mut pos = 0; - let mut nodes: Vec<(AstNode, Range)> = vec![]; + let mut nodes: Ast = vec![]; loop { let mut statements = vec![]; @@ -148,7 +146,25 @@ impl<'a> Parser<'a> { pos += 1; }, - Token::Exit => nodes.push((AstNode::Exit, token.1..token.0.len())), + Token::Exit => loop { + let token = tokens.get(pos); + + if token.is_none() { + break; + } + + let token = token?; + + if token.0 == Token::Semicolon { + statements.push(token); + nodes.push(self.parse(statements)?); + break; + } + + statements.push(token); + + pos += 1; + }, Token::Semicolon => {} t => { error( @@ -178,38 +194,33 @@ impl<'a> Parser<'a> { let t = &stream.next()?; if let Token::Identifier(ident) = &t.0 { let t = stream.next()?; - if t.0 != Token::Equal { + let datatype = if let Token::DataType(datatype) = t.0 { + datatype + } else { error( self.name, self.file, - "0001", - &format!("expected `=` found {}", t.0.as_string()), - "use `=` here", - &(t.1 - 1..t.1 + t.0.len() - 1), - ); + "E0010", + "expected data type", + "expected data type", + &(t.1..t.1 + t.0.len()), + ) }; + self.check(stream.next()?, Token::Equal); + let mut tokens = stream.collect::>(); let t = tokens.pop()?; - if t.0 != Token::Semicolon { - error( - self.name, - self.file, - "0001", - &format!("expected `;` found {}", t.0.as_string()), - "use `;` here", - &(t.1 - 1..t.1 + t.0.len() - 1), - ); - } + self.check(t, Token::Semicolon); let tokens = tokens.iter().map(|f| **f).collect::>(); let (expression, _) = self.pratt_parser(tokens.into_iter().peekable(), 0); ( - AstNode::Assignment(ident.to_string(), expression), + AstNode::Assignment(ident.to_string(), datatype, expression), token.1..t.1, ) } else { @@ -219,44 +230,38 @@ impl<'a> Parser<'a> { "0001", &format!("expected identifier found {}", t.0.as_string()), "use an identifier here", - &(t.1 - 1..t.1 + t.0.len() - 1), + &(t.1..t.1 + t.0.len()), ); } } else if let Token::Identifier(ident) = &token.0 { - let t = &stream.next()?; - - if t.0 != Token::Equal { + let t = stream.next()?; + let datatype = if let Token::DataType(datatype) = t.0 { + datatype + } else { error( self.name, self.file, - "0001", - &format!("unexpected punctuator {}", t.0.as_string()), - "for declaring a value `=` should be used", - &(t.1 - 1..t.1 + t.0.len() - 1), - ); + "E0010", + "expected data type", + "expected data type", + &(t.1..t.1 + t.0.len()), + ) }; + self.check(stream.next()?, Token::Equal); + let mut tokens = stream.collect::>(); let t = tokens.pop()?; - if t.0 != Token::Semicolon { - error( - self.name, - self.file, - "0001", - &format!("expected `;` found {}", t.0.as_string()), - "use `;` here", - &(t.1 - 1..t.1 + t.0.len() - 1), - ); - } + self.check(t, Token::Semicolon); let tokens = tokens.iter().map(|f| **f).collect::>(); let (expression, _) = self.pratt_parser(tokens.into_iter().peekable(), 0); ( - AstNode::ReAssignment(ident.to_string(), expression), + AstNode::ReAssignment(ident.to_string(), datatype, expression), token.1..t.1, ) } else if token.0 == Token::If { @@ -278,16 +283,7 @@ impl<'a> Parser<'a> { let t = statements.pop()?; - if t.0 != Token::RCurly { - error( - self.name, - self.file, - "0001", - &format!("expected `}}` found {}", t.0.as_string()), - "use `}` here", - &(t.1 - 1..t.1 + t.0.len() - 1), - ); - } + self.check(&t, Token::RCurly); let tokens = tokens.iter().map(|f| **f).collect::>(); @@ -300,18 +296,7 @@ impl<'a> Parser<'a> { } else if token.0 == Token::Loop { let mut statements = vec![]; - let t = &stream.next()?; - - if t.0 != Token::LCurly { - error( - self.name, - self.file, - "0001", - &format!("expected `{{` found {}", t.0.as_string()), - "use `{` here", - &(t.1 - 1..t.1 + t.0.len() - 1), - ); - }; + self.check(stream.next()?, Token::LCurly); for token in stream { statements.push((*token).to_owned()); @@ -319,30 +304,11 @@ impl<'a> Parser<'a> { let t = statements.pop()?; - if t.0 != Token::RCurly { - error( - self.name, - self.file, - "0001", - &format!("expected `}}` found {}", t.0.as_string()), - "use `}` here", - &(t.1 - 1..t.1 + t.0.len() - 1), - ); - }; + self.check(&t, Token::RCurly); (AstNode::Loop(self.match_tokens(statements)?), token.1..t.1) } else if let Token::FunctionName(ident) = &token.0 { - let t = &stream.next()?; - if t.0 != Token::LParen { - error( - self.name, - self.file, - "0001", - &format!("expected `(` found {}", t.0.as_string()), - "use `(` here", - &(t.1 - 1..t.1 + t.0.len() - 1), - ); - }; + self.check(stream.next()?, Token::LParen); let tokens = stream.map(|f| f.to_owned()).collect::>(); let mut params = vec![]; @@ -414,22 +380,13 @@ impl<'a> Parser<'a> { } if let Token::Identifier(name) = token { - params.push(name.to_string()); + if let Token::DataType(datatype) = stream.next()?.0 { + params.push(Param::new(name.to_string(), datatype)); + } } } - let t = &stream.next()?; - - if t.0 != Token::LCurly { - error( - self.name, - self.file, - "0001", - &format!("expected `{{` found {}", t.0.as_string()), - "use `{` here", - &(t.1 - 1..t.1 + t.0.len() - 1), - ); - }; + self.check(stream.next()?, Token::LCurly); let mut statements = vec![]; @@ -447,16 +404,7 @@ impl<'a> Parser<'a> { let t = statements.pop()?; - if t.0 != Token::RCurly { - error( - self.name, - self.file, - "0001", - &format!("expected `}}` found {}", t.0.as_string()), - "use `}` here", - &(t.1 - 1..t.1 + t.0.len() - 1), - ); - }; + self.check(&t, Token::RCurly); ( AstNode::FunctionDeclaration( @@ -473,7 +421,7 @@ impl<'a> Parser<'a> { "0001", &format!("expected name of function found {}", t.0.as_string()), "use function name here", - &(t.1 - 1..t.1 + t.0.len() - 1), + &(t.1..t.1 + t.0.len()), ); } } else if token.0 == Token::Return { @@ -481,22 +429,25 @@ impl<'a> Parser<'a> { let t = tokens.pop()?; - if t.0 != Token::Semicolon { - error( - self.name, - self.file, - "0001", - &format!("expected `;` found {}", t.0.as_string()), - "use `;` here", - &(t.1 - 1..t.1 + t.0.len() - 1), - ); - } + self.check(t, Token::Semicolon); let tokens = tokens.iter().map(|f| **f).collect::>(); let (expression, _) = self.pratt_parser(tokens.into_iter().peekable(), 0); (AstNode::Return(expression), token.1..t.1) + } else if token.0 == Token::Exit { + let mut tokens = stream.collect::>(); + + let t = tokens.pop()?; + + self.check(t, Token::Semicolon); + + let tokens = tokens.iter().map(|f| **f).collect::>(); + + let (expression, _) = self.pratt_parser(tokens.into_iter().peekable(), 0); + + (AstNode::Exit(expression), token.1..t.1) } else { error( self.name, @@ -511,12 +462,26 @@ impl<'a> Parser<'a> { Some(node) } + pub fn check(&self, t1: &(Token, usize), t2: Token) -> bool { + if t1.0 != t2 { + error( + self.name, + self.file, + "0001", + &format!("expected `{}` found {}", t2.as_string(), t1.0.as_string()), + &format!("use `{}` here", t2.as_string()), + &(t1.1 - 1..t1.1 + t2.len() - 1), + ); + }; + + true + } + pub fn pratt_parser( &'a self, mut lexer: Peekable>, prec: u16, ) -> (Expression, Peekable>) { - println!("{lexer:?}"); let token = &lexer.next().unwrap(); let mut expr: Option = None; @@ -536,8 +501,8 @@ impl<'a> Parser<'a> { expr = Some(exp); } Token::Subtraction => { - if let Token::Integer(i) = token.0 { - expr = Some(Expression::Integer(-i)); + if let Token::Int(i) = token.0 { + expr = Some(Expression::Int(-i)); } } Token::FunctionName(f) => { @@ -549,7 +514,7 @@ impl<'a> Parser<'a> { "0001", &format!("expected `(` found {}", t.0.as_string()), "use `(` here", - &(t.1 - 1..t.1 + t.0.len() - 1), + &(t.1..t.1 + t.0.len()), ); }; @@ -578,7 +543,7 @@ impl<'a> Parser<'a> { } let mut params = vec![]; - let mut expression: Vec<(Token, usize)> = vec![]; + let mut expression: Tokens = vec![]; for token in tokens { if token.0 == Token::RParen { @@ -607,8 +572,8 @@ impl<'a> Parser<'a> { expr = Some(Expression::FunctionCall(f.to_string(), params)); } _ => { - if let Token::Integer(i) = token.0 { - expr = Some(Expression::Integer(i)); + if let Token::Int(i) = token.0 { + expr = Some(Expression::Int(i)); } } }; @@ -650,8 +615,6 @@ impl<'a> Parser<'a> { ); } - println!("{expr:?}"); - (expr.unwrap(), lexer) } diff --git a/src/standardlibrary.rs b/src/standardlibrary.rs index 04b48c1..0536c11 100644 --- a/src/standardlibrary.rs +++ b/src/standardlibrary.rs @@ -1,4 +1,4 @@ -use crate::datatype::Data; +use crate::data::Data; pub struct StandardLibrary; @@ -27,19 +27,19 @@ impl StandardLibrary { } mod types { - use crate::datatype::Data; + use crate::data::Data; pub fn int(data: Data) -> Data { match data { - Data::Integer(_) => data, - Data::Bool(b) => Data::Integer(b as i64), - Data::Str(s) => Data::Integer(s.parse::().unwrap()), + Data::Int(_) => data, + Data::Bool(b) => Data::Int(b as i64), + Data::Str(s) => Data::Int(s.parse::().unwrap()), } } pub fn bool(data: Data) -> Data { match data { - Data::Integer(i) => Data::Bool(i != 0), + Data::Int(i) => Data::Bool(i != 0), Data::Bool(_) => data, Data::Str(s) => Data::Bool(s.parse::().unwrap()), } @@ -47,7 +47,7 @@ mod types { pub fn str(data: Data) -> Data { match data { - Data::Integer(i) => Data::Str(i.to_string()), + Data::Int(i) => Data::Str(i.to_string()), Data::Bool(b) => Data::Str(b.to_string()), Data::Str(_) => data, } @@ -55,7 +55,7 @@ mod types { } mod io { - use crate::datatype::Data; + use crate::data::Data; use std::io::stdin; pub fn read() -> Data { @@ -73,7 +73,7 @@ mod io { pub fn print(datas: Vec) { for data in datas { match data { - Data::Integer(i) => print!("{i}"), + Data::Int(i) => print!("{i}"), Data::Bool(b) => print!("{b}"), Data::Str(s) => print!("{s}"), } diff --git a/src/token.rs b/src/token.rs index d172e57..ce949e8 100644 --- a/src/token.rs +++ b/src/token.rs @@ -1,5 +1,9 @@ use std::fmt::Debug; +use crate::data::DataType; + +pub type Tokens = Vec<(Token, usize)>; + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum Token { Let, @@ -23,9 +27,10 @@ pub enum Token { Identifier(String), FunctionName(String), + DataType(DataType), Str(String), - Integer(i64), + Int(i64), Bool(bool), Semicolon, @@ -64,9 +69,16 @@ impl Token { Token::Identifier(ident) => ident.to_string(), Token::FunctionName(fname) => fname.to_string(), + Token::DataType(datatype) => { + match datatype { + DataType::Str => String::from("Str"), + DataType::Int => String::from("Int"), + DataType::Bool => String::from("Bool"), + } + }, Token::Str(string) => string.to_string(), - Token::Integer(i) => format!("{i}"), + Token::Int(i) => format!("{i}"), Token::Bool(b) => format!("{b}"), Token::Semicolon => String::from(";"),