diff --git a/lykiadb-lang/benches/parsing.rs b/lykiadb-lang/benches/parsing.rs index 9b7c861c..db27a9fc 100644 --- a/lykiadb-lang/benches/parsing.rs +++ b/lykiadb-lang/benches/parsing.rs @@ -1,5 +1,9 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use lykiadb_lang::{parser::{program::Program, resolver::Resolver, Parser}, tokenizer::scanner::Scanner, Locals, Scopes}; +use lykiadb_lang::{ + parser::{program::Program, resolver::Resolver, Parser}, + tokenizer::scanner::Scanner, + Locals, Scopes, +}; use rustc_hash::FxHashMap; pub struct ParserBenchmark { @@ -7,6 +11,11 @@ pub struct ParserBenchmark { locals: Locals, } +impl Default for ParserBenchmark { + fn default() -> Self { + Self::new() + } +} impl ParserBenchmark { pub fn new() -> ParserBenchmark { @@ -49,16 +58,15 @@ fn runtime() { EXCEPT SELECT * FROM books; - ".to_string(); + " + .to_string(); let mut parser = black_box(ParserBenchmark::new()); black_box(parser.process(black_box(&content))); } fn bench(c: &mut Criterion) { let mut group = c.benchmark_group("sample-size-example"); - group.bench_function("2-way join", |b| { - b.iter(|| runtime()) - }); + group.bench_function("2-way join", |b| b.iter(runtime)); group.finish(); } diff --git a/lykiadb-lang/src/ast/expr.rs b/lykiadb-lang/src/ast/expr.rs index 8532830d..258e9ab9 100644 --- a/lykiadb-lang/src/ast/expr.rs +++ b/lykiadb-lang/src/ast/expr.rs @@ -3,16 +3,12 @@ use serde::{Deserialize, Serialize}; use std::{fmt::Display, sync::Arc}; -use crate::{Identifier, Span, Spanned}; - use super::{ sql::{SqlDelete, SqlInsert, SqlSelect, SqlUpdate}, stmt::Stmt, - AstNode, + AstNode, Identifier, Literal, Span, Spanned, }; -use crate::Literal; - #[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)] #[serde(tag = "@type")] pub enum Operation { @@ -497,7 +493,7 @@ impl Expr { pub mod test { use std::collections::HashSet; - use crate::{ast::expr::Expr, Literal, Span}; + use crate::ast::expr::Expr; use super::*; @@ -703,12 +699,10 @@ pub mod test { }); assert_eq!(visited, vec![3, 1, 2]); } - } #[test] fn test_expr_get_id() { - // Test Variable let var_expr = Expr::Variable { name: Identifier::new("test_var", false), @@ -822,7 +816,6 @@ pub mod test { fn test_expr_get_span() { let test_span = Span::default(); - // Test Variable let var_expr = Expr::Variable { name: Identifier::new("test_var", false), @@ -1073,10 +1066,7 @@ pub mod test { fn test_function_display() { let func = Expr::Function { name: Some(Identifier::new("test_func", false)), - parameters: vec![ - Identifier::new("a", false), - Identifier::new("b", false), - ], + parameters: vec![Identifier::new("a", false), Identifier::new("b", false)], body: Arc::new(vec![]), span: Span::default(), id: 1, @@ -1139,7 +1129,6 @@ pub mod test { assert_eq!(call.to_string(), "test_func(Num(1.0), Num(2.0))"); } - pub fn create_simple_add_expr(id: usize, left: f64, right: f64) -> Expr { Expr::Binary { left: Box::new(Expr::Literal { diff --git a/lykiadb-lang/src/ast/mod.rs b/lykiadb-lang/src/ast/mod.rs index b7962967..eccc5d78 100644 --- a/lykiadb-lang/src/ast/mod.rs +++ b/lykiadb-lang/src/ast/mod.rs @@ -1,4 +1,13 @@ -use crate::Spanned; +use std::{ + fmt::{Display, Formatter}, + hash::Hash, + sync::Arc, +}; + +use derivative::Derivative; +use expr::Expr; +use rustc_hash::FxHashMap; +use serde::{Deserialize, Serialize}; pub mod expr; pub mod sql; @@ -8,3 +17,99 @@ pub mod visitor; pub trait AstNode: Spanned { fn get_id(&self) -> usize; } + +#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize, Hash)] +pub struct Span { + pub start: usize, + pub end: usize, + pub line: u32, + pub line_end: u32, +} + +pub trait Spanned { + fn get_span(&self) -> Span; +} + +impl Spanned for Span { + fn get_span(&self) -> Span { + *self + } +} + +impl Span { + pub fn merge(&self, other: &Span) -> Span { + Span { + start: self.start.min(other.start), + end: self.end.max(other.end), + line: self.line.min(other.line), + line_end: self.line_end.min(other.line_end), + } + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub enum Literal { + Str(Arc), + Num(f64), + Bool(bool), + Undefined, + Object(FxHashMap>), + Array(Vec), + NaN, + Null, +} + +impl Literal { + pub fn as_str(&self) -> Option<&str> { + match self { + Literal::Str(s) => Some(s), + _ => None, + } + } +} + +impl Hash for Literal { + fn hash(&self, state: &mut H) { + match self { + Literal::Str(s) => s.hash(state), + Literal::Num(n) => n.to_bits().hash(state), + Literal::Bool(b) => b.hash(state), + Literal::Object(o) => (o as *const _ as usize).hash(state), + Literal::Array(a) => a.hash(state), + // + Literal::Undefined => "undefined".hash(state), + Literal::NaN => "NaN".hash(state), + Literal::Null => "null".hash(state), + } + } +} + +impl Eq for Literal {} + +#[derive(Debug, Clone, Serialize, Deserialize, Derivative)] +#[serde(tag = "@type")] +#[derivative(Eq, PartialEq, Hash)] +pub struct Identifier { + pub name: String, + pub dollar: bool, + #[serde(skip)] + #[derivative(PartialEq = "ignore")] + #[derivative(Hash = "ignore")] + pub span: Span, +} + +impl Identifier { + pub fn new(name: &str, dollar: bool) -> Self { + Identifier { + name: name.to_string(), + dollar, + span: Span::default(), + } + } +} + +impl Display for Identifier { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "{}", self.name) + } +} diff --git a/lykiadb-lang/src/ast/sql.rs b/lykiadb-lang/src/ast/sql.rs index 9082cece..9ad69b74 100644 --- a/lykiadb-lang/src/ast/sql.rs +++ b/lykiadb-lang/src/ast/sql.rs @@ -1,7 +1,6 @@ -use crate::Identifier; use serde::{Deserialize, Serialize}; -use super::expr::Expr; +use super::{expr::Expr, Identifier}; // Enums diff --git a/lykiadb-lang/src/ast/stmt.rs b/lykiadb-lang/src/ast/stmt.rs index 73bc5b03..0d77532a 100644 --- a/lykiadb-lang/src/ast/stmt.rs +++ b/lykiadb-lang/src/ast/stmt.rs @@ -1,9 +1,7 @@ use derivative::Derivative; use serde::{Deserialize, Serialize}; -use crate::{Identifier, Span, Spanned}; - -use super::expr::Expr; +use super::{expr::Expr, Identifier, Span, Spanned}; #[derive(Debug, Serialize, Deserialize, Clone, Derivative)] #[serde(tag = "@type")] @@ -106,11 +104,9 @@ impl Spanned for Stmt { mod test { use std::collections::HashSet; - use crate::{ - ast::{ - expr::{test::create_simple_add_expr, Expr}, - stmt::Stmt, - }, + use crate::ast::{ + expr::{test::create_simple_add_expr, Expr}, + stmt::Stmt, Span, }; diff --git a/lykiadb-lang/src/lib.rs b/lykiadb-lang/src/lib.rs index 978af0b0..fdd2ab4e 100644 --- a/lykiadb-lang/src/lib.rs +++ b/lykiadb-lang/src/lib.rs @@ -1,13 +1,11 @@ -use std::{ - fmt::{Display, Formatter, Result}, - hash::Hash, - sync::Arc, +use parser::{ + program::Program, + resolver::{ResolveError, Resolver}, + ParseError, Parser, }; - -use ast::expr::Expr; -use derivative::Derivative; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; +use tokenizer::scanner::{ScanError, Scanner}; pub mod ast; pub mod parser; @@ -16,98 +14,60 @@ pub mod tokenizer; pub type Scopes = Vec>; pub type Locals = FxHashMap; -#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize, Hash)] -pub struct Span { - pub start: usize, - pub end: usize, - pub line: u32, - pub line_end: u32, +#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] +pub enum LangError { + Parse(ParseError), + Scan(ScanError), + Resolve(ResolveError), } -pub trait Spanned { - fn get_span(&self) -> Span; +impl From for LangError { + fn from(err: ParseError) -> Self { + LangError::Parse(err) + } } -impl Spanned for Span { - fn get_span(&self) -> Span { - *self +impl From for LangError { + fn from(err: ScanError) -> Self { + LangError::Scan(err) } } -impl Span { - pub fn merge(&self, other: &Span) -> Span { - Span { - start: self.start.min(other.start), - end: self.end.max(other.end), - line: self.line.min(other.line), - line_end: self.line_end.min(other.line_end), - } +impl From for LangError { + fn from(err: ResolveError) -> Self { + LangError::Resolve(err) } } -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub enum Literal { - Str(Arc), - Num(f64), - Bool(bool), - Undefined, - Object(FxHashMap>), - Array(Vec), - NaN, - Null, +pub struct SourceProcessor { + scopes: Scopes, + locals: Locals, } -impl Literal { - pub fn as_str(&self) -> Option<&str> { - match self { - Literal::Str(s) => Some(s), - _ => None, - } +impl Default for SourceProcessor { + fn default() -> Self { + Self::new() } } -impl Hash for Literal { - fn hash(&self, state: &mut H) { - match self { - Literal::Str(s) => s.hash(state), - Literal::Num(n) => n.to_bits().hash(state), - Literal::Bool(b) => b.hash(state), - Literal::Object(o) => (o as *const _ as usize).hash(state), - Literal::Array(a) => a.hash(state), - // - Literal::Undefined => "undefined".hash(state), - Literal::NaN => "NaN".hash(state), - Literal::Null => "null".hash(state), +impl SourceProcessor { + pub fn new() -> SourceProcessor { + SourceProcessor { + scopes: vec![], + locals: FxHashMap::default(), } } -} -impl Eq for Literal {} + pub fn process(&mut self, source: &str) -> Result { + let tokens = Scanner::scan(source)?; + let mut program = Parser::parse(&tokens)?; + let mut resolver = Resolver::new(self.scopes.clone(), &program, Some(self.locals.clone())); + let (scopes, locals) = resolver.resolve()?; -#[derive(Debug, Clone, Serialize, Deserialize, Derivative)] -#[serde(tag = "@type")] -#[derivative(Eq, PartialEq, Hash)] -pub struct Identifier { - pub name: String, - pub dollar: bool, - #[serde(skip)] - #[derivative(PartialEq = "ignore")] - #[derivative(Hash = "ignore")] - pub span: Span, -} - -impl Identifier { - pub fn new(name: &str, dollar: bool) -> Self { - Identifier { - name: name.to_string(), - dollar, - span: Span::default(), - } - } -} + self.scopes = scopes; + self.locals.clone_from(&locals); + program.set_locals(self.locals.clone()); -impl Display for Identifier { - fn fmt(&self, f: &mut Formatter) -> Result { - write!(f, "{}", self.name) + Ok(program) } } diff --git a/lykiadb-lang/src/parser/mod.rs b/lykiadb-lang/src/parser/mod.rs index 08b65db9..8289eb9b 100644 --- a/lykiadb-lang/src/parser/mod.rs +++ b/lykiadb-lang/src/parser/mod.rs @@ -3,11 +3,11 @@ use self::program::Program; use super::ast::expr::{Expr, Operation}; use super::ast::stmt::Stmt; use crate::ast::expr::RangeKind; +use crate::ast::{Literal, Span, Spanned}; use crate::tokenizer::token::{ Keyword::*, SqlKeyword, SqlKeyword::*, Symbol::*, Token, TokenType, TokenType::*, }; use crate::{kw, skw, sym}; -use crate::{Literal, Span, Spanned}; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; use std::sync::Arc; @@ -616,7 +616,7 @@ impl<'a> Parser<'a> { && self.in_select_depth > 0 { let head = name.clone(); - let mut tail: Vec = vec![]; + let mut tail: Vec = vec![]; while self.match_next(&sym!(Dot)) { let identifier = self.expected(&Identifier { dollar: false })?.clone(); tail.push(identifier.extract_identifier().unwrap()); @@ -912,7 +912,7 @@ macro_rules! optional_with_expected { }; } -impl<'a> Parser<'a> { +impl Parser<'_> { fn sql_insert(&mut self) -> ParseResult> { if !self.match_next(&skw!(Insert)) { return self.sql_update(); diff --git a/lykiadb-lang/src/parser/resolver.rs b/lykiadb-lang/src/parser/resolver.rs index 04eeaafe..8d53e569 100644 --- a/lykiadb-lang/src/parser/resolver.rs +++ b/lykiadb-lang/src/parser/resolver.rs @@ -1,8 +1,9 @@ use crate::ast::expr::Expr; use crate::ast::stmt::Stmt; use crate::ast::visitor::VisitorMut; -use crate::{Identifier, Literal}; -use crate::{Locals, Scopes, Span}; +use crate::ast::Span; +use crate::ast::{Identifier, Literal}; +use crate::{Locals, Scopes}; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; @@ -34,11 +35,7 @@ impl<'a> Resolver<'a> { ) -> Resolver<'a> { Resolver { scopes, - locals: if let Some(previous_locals) = previous_locals { - previous_locals - } else { - FxHashMap::default() - }, + locals: previous_locals.unwrap_or_default(), program, } } @@ -91,7 +88,7 @@ impl<'a> Resolver<'a> { } } -impl<'a> VisitorMut<(), ResolveError> for Resolver<'a> { +impl VisitorMut<(), ResolveError> for Resolver<'_> { fn visit_expr(&mut self, e: &Expr) -> Result<(), ResolveError> { match e { Expr::Literal { value, .. } => match value { diff --git a/lykiadb-lang/src/tokenizer/scanner.rs b/lykiadb-lang/src/tokenizer/scanner.rs index 83d2c9b6..72ec97d6 100644 --- a/lykiadb-lang/src/tokenizer/scanner.rs +++ b/lykiadb-lang/src/tokenizer/scanner.rs @@ -1,10 +1,10 @@ use serde::{Deserialize, Serialize}; +use crate::ast::{Literal::*, Span}; use crate::sym; use crate::tokenizer::token::Symbol::*; use crate::tokenizer::token::TokenType::{Eof, Identifier}; use crate::tokenizer::token::*; -use crate::{Literal::*, Span}; use std::iter::{Enumerate, Peekable}; use std::str::Chars; use std::sync::Arc; @@ -21,7 +21,7 @@ pub enum ScanError { MalformedNumberLiteral { span: Span }, } -impl<'a> Scanner<'a> { +impl Scanner<'_> { pub fn scan(source: &str) -> Result, ScanError> { let mut scanner = Scanner { chars: source.chars().enumerate().peekable(), diff --git a/lykiadb-lang/src/tokenizer/token.rs b/lykiadb-lang/src/tokenizer/token.rs index 3bfc33df..317aec14 100644 --- a/lykiadb-lang/src/tokenizer/token.rs +++ b/lykiadb-lang/src/tokenizer/token.rs @@ -1,7 +1,8 @@ -use crate::{Identifier, Literal, Span}; use phf::phf_map; use serde::{Deserialize, Serialize}; +use crate::ast::{Identifier, Literal, Span}; + #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub enum Symbol { Comma, diff --git a/lykiadb-server/Cargo.toml b/lykiadb-server/Cargo.toml index e40f3279..ac47f86e 100644 --- a/lykiadb-server/Cargo.toml +++ b/lykiadb-server/Cargo.toml @@ -28,6 +28,7 @@ tokio-stream = { version = "~0.1.6", features = ["net"] } tracing = "0.1" tracing-subscriber = "0.3" pretty_assertions = "1.4.1" +string-interner = "0.18.0" [dev-dependencies] criterion = { version = "0.4", features = ["html_reports"] } @@ -35,4 +36,4 @@ test_each_file = "0.3.4" [[bench]] name = "interpreter" -harness = false +harness = false \ No newline at end of file diff --git a/lykiadb-server/src/comm/mod.rs b/lykiadb-server/src/comm/mod.rs index 512a2f99..1adf5db0 100644 --- a/lykiadb-server/src/comm/mod.rs +++ b/lykiadb-server/src/comm/mod.rs @@ -16,7 +16,6 @@ use crate::engine::error::ExecutionError; #[derive(Debug, Clone, Serialize, Deserialize)] pub enum Request { Run(String), - Ast(String), } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -70,13 +69,6 @@ impl ServerSession { let start = Instant::now(); match &message { Message::Request(req) => match req { - Request::Ast(source) => { - let ast = self.runtime.ast(source); - self.conn - .write(Message::Response(Response::Program(ast.unwrap()))) - .await - .unwrap(); - } Request::Run(command) => { let execution = self.runtime.interpret(command); let response = if execution.is_ok() { diff --git a/lykiadb-server/src/engine/error.rs b/lykiadb-server/src/engine/error.rs index 28f4a967..c8776c15 100644 --- a/lykiadb-server/src/engine/error.rs +++ b/lykiadb-server/src/engine/error.rs @@ -3,18 +3,12 @@ use std::fmt::{Display, Formatter, Result}; use crate::{plan::PlannerError, value::environment::EnvironmentError}; use super::interpreter::InterpretError; -use lykiadb_lang::{ - parser::{resolver::ResolveError, ParseError}, - tokenizer::scanner::ScanError, - Span, -}; +use lykiadb_lang::{ast::Span, parser::ParseError, tokenizer::scanner::ScanError, LangError}; use serde::{Deserialize, Serialize}; #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] pub enum ExecutionError { - Scan(ScanError), - Parse(ParseError), - Resolve(ResolveError), + Lang(LangError), Interpret(InterpretError), Environment(EnvironmentError), Plan(PlannerError), @@ -26,19 +20,12 @@ impl Display for ExecutionError { } } -impl From for ExecutionError { - fn from(err: ParseError) -> Self { - ExecutionError::Parse(err) - } -} - -impl From for ExecutionError { - fn from(err: ScanError) -> Self { - ExecutionError::Scan(err) - } -} - -pub fn report_error(source_name: &str, source: &str, error: ExecutionError, mut writer: impl std::io::Write) { +pub fn report_error( + source_name: &str, + source: &str, + error: ExecutionError, + mut writer: impl std::io::Write, +) { use ariadne::{Color, Label, Report, ReportKind, Source}; // Generate & choose some colours for each of our elements @@ -54,28 +41,29 @@ pub fn report_error(source_name: &str, source: &str, error: ExecutionError, mut .with_color(out), ) .finish() - .write((source_name, Source::from(&source)), &mut writer).unwrap(); + .write((source_name, Source::from(&source)), &mut writer) + .unwrap(); }; match error { - ExecutionError::Scan(ScanError::UnexpectedCharacter { span }) => { + ExecutionError::Lang(LangError::Scan(ScanError::UnexpectedCharacter { span })) => { print("Unexpected character", "Remove this character", span); } - ExecutionError::Scan(ScanError::UnterminatedString { span }) => { + ExecutionError::Lang(LangError::Scan(ScanError::UnterminatedString { span })) => { print( "Unterminated string", "Terminate the string with a double quote (\").", span, ); } - ExecutionError::Scan(ScanError::MalformedNumberLiteral { span }) => { + ExecutionError::Lang(LangError::Scan(ScanError::MalformedNumberLiteral { span })) => { print( "Malformed number literal", "Make sure that number literal is up to spec.", span, ); } - ExecutionError::Parse(ParseError::MissingToken { token, expected }) => { + ExecutionError::Lang(LangError::Parse(ParseError::MissingToken { token, expected })) => { print( "Missing token", &format!( @@ -86,21 +74,17 @@ pub fn report_error(source_name: &str, source: &str, error: ExecutionError, mut token.span, ); } - ExecutionError::Parse(ParseError::NoTokens) => { - print( - "There is nothing to parse", - "", - Span::default(), - ); + ExecutionError::Lang(LangError::Parse(ParseError::NoTokens)) => { + print("There is nothing to parse", "", Span::default()); } - ExecutionError::Parse(ParseError::InvalidAssignmentTarget { left }) => { + ExecutionError::Lang(LangError::Parse(ParseError::InvalidAssignmentTarget { left })) => { print( "Invalid assignment target", &format!("No values can be assigned to {}", left.lexeme.unwrap()), left.span, ); } - ExecutionError::Parse(ParseError::UnexpectedToken { token }) => { + ExecutionError::Lang(LangError::Parse(ParseError::UnexpectedToken { token })) => { print( "Unexpected token", &format!( @@ -167,10 +151,13 @@ pub fn report_error(source_name: &str, source: &str, error: ExecutionError, mut } #[cfg(test)] mod tests { - use core::panic; use super::*; - use lykiadb_lang::{kw, sym, tokenizer::token::{Keyword, Symbol, Token, TokenType}, Identifier, Literal}; + use lykiadb_lang::{ + ast::{Identifier, Literal}, + kw, sym, + tokenizer::token::{Keyword, Symbol, Token, TokenType}, + }; fn capture_error_output(filename: &str, source: &str, error: ExecutionError) -> String { let mut output = Vec::new(); @@ -178,19 +165,18 @@ mod tests { String::from_utf8(output).unwrap() } - // Scanner Error Tests #[test] fn test_scanner_unterminated_string() { let source = r#"let x = "unterminated"#; - let error = ExecutionError::Scan(ScanError::UnterminatedString { + let error = ExecutionError::Lang(LangError::Scan(ScanError::UnterminatedString { span: Span { start: 8, end: 21, line: 0, line_end: 0, }, - }); + })); let output = capture_error_output("test.txt", source, error); assert!(output.contains("Unterminated string")); @@ -200,14 +186,14 @@ mod tests { #[test] fn test_scanner_malformed_number() { let source = "let x = 123.456.789"; - let error = ExecutionError::Scan(ScanError::MalformedNumberLiteral { + let error = ExecutionError::Lang(LangError::Scan(ScanError::MalformedNumberLiteral { span: Span { start: 8, end: 19, line: 0, line_end: 0, }, - }); + })); let output = capture_error_output("test.txt", source, error); assert!(output.contains("Malformed number literal")); @@ -218,7 +204,7 @@ mod tests { #[test] fn test_parser_missing_token() { let source = "var x = "; - let error = ExecutionError::Parse(ParseError::MissingToken { + let error = ExecutionError::Lang(LangError::Parse(ParseError::MissingToken { token: Token { tok_type: kw!(Keyword::Var), lexeme: Some("var".to_string()), @@ -230,8 +216,8 @@ mod tests { }, literal: None, }, - expected: TokenType::Identifier { dollar: true } - }); + expected: TokenType::Identifier { dollar: true }, + })); let output = capture_error_output("test.txt", source, error); assert!(output.contains("Missing token")); @@ -241,7 +227,7 @@ mod tests { #[test] fn test_parser_no_tokens() { let source = ""; - let error = ExecutionError::Parse(ParseError::NoTokens); + let error = ExecutionError::Lang(LangError::Parse(ParseError::NoTokens)); let output = capture_error_output("test.txt", source, error); @@ -251,7 +237,7 @@ mod tests { #[test] fn test_parser_unexpected_token() { let source = "let x = ;"; - let error = ExecutionError::Parse(ParseError::UnexpectedToken { + let error = ExecutionError::Lang(LangError::Parse(ParseError::UnexpectedToken { token: Token { tok_type: sym!(Symbol::Semicolon), lexeme: Some(";".to_string()), @@ -263,7 +249,7 @@ mod tests { }, literal: None, }, - }); + })); let output = capture_error_output("test.txt", source, error); assert!(output.contains("Unexpected token")); @@ -290,7 +276,7 @@ mod tests { #[test] fn test_parser_invalid_assignment() { let source = "5 = 10"; - let error = ExecutionError::Parse(ParseError::InvalidAssignmentTarget { + let error = ExecutionError::Lang(LangError::Parse(ParseError::InvalidAssignmentTarget { left: Token { tok_type: TokenType::Num, lexeme: Some("5".to_string()), @@ -302,7 +288,7 @@ mod tests { }, literal: Some(Literal::Num(5.0)), }, - }); + })); let output = capture_error_output("test.txt", source, error); assert!(output.contains("Invalid assignment target")); @@ -390,14 +376,14 @@ mod tests { #[test] fn test_scan_error_reporting() { let source = "let x = @"; - let error = ExecutionError::Scan(ScanError::UnexpectedCharacter { + let error = ExecutionError::Lang(LangError::Scan(ScanError::UnexpectedCharacter { span: Span { start: 8, end: 9, line: 0, line_end: 0, }, - }); + })); let output = capture_error_output("test.txt", source, error); assert!(output.contains("Unexpected character")); diff --git a/lykiadb-server/src/engine/interpreter.rs b/lykiadb-server/src/engine/interpreter.rs index f7c9f1ac..e0a72afe 100644 --- a/lykiadb-server/src/engine/interpreter.rs +++ b/lykiadb-server/src/engine/interpreter.rs @@ -1,16 +1,15 @@ use lykiadb_lang::ast::expr::{Expr, Operation, RangeKind}; use lykiadb_lang::ast::stmt::Stmt; use lykiadb_lang::ast::visitor::VisitorMut; +use lykiadb_lang::ast::{Literal, Span, Spanned}; use lykiadb_lang::parser::program::Program; -use lykiadb_lang::parser::resolver::Resolver; -use lykiadb_lang::parser::Parser; -use lykiadb_lang::tokenizer::scanner::Scanner; -use lykiadb_lang::Span; -use lykiadb_lang::Spanned; -use lykiadb_lang::{Literal, Locals, Scopes}; +use lykiadb_lang::{LangError, SourceProcessor}; use pretty_assertions::assert_eq; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; +use string_interner::backend::StringBackend; +use string_interner::symbol::SymbolU32; +use string_interner::StringInterner; use super::error::ExecutionError; use super::stdlib::stdlib; @@ -18,45 +17,12 @@ use super::stdlib::stdlib; use crate::plan::planner::Planner; use crate::util::{alloc_shared, Shared}; use crate::value::callable::{Callable, CallableKind, Function, Stateful}; -use crate::value::environment::{EnvId, Environment}; +use crate::value::environment::EnvironmentFrame; use crate::value::{eval::eval_binary, RV}; use std::sync::Arc; use std::vec; -pub struct SourceProcessor { - scopes: Scopes, - locals: Locals, -} - -impl Default for SourceProcessor { - fn default() -> Self { - Self::new() - } -} - -impl SourceProcessor { - pub fn new() -> SourceProcessor { - SourceProcessor { - scopes: vec![], - locals: FxHashMap::default(), - } - } - - pub fn process(&mut self, source: &str) -> Result { - let tokens = Scanner::scan(source)?; - let mut program = Parser::parse(&tokens)?; - let mut resolver = Resolver::new(self.scopes.clone(), &program, Some(self.locals.clone())); - let (scopes, locals) = resolver.resolve().unwrap(); - - self.scopes = scopes; - self.locals.clone_from(&locals); - program.set_locals(self.locals.clone()); - - Ok(program) - } -} - #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] pub enum InterpretError { NotCallable { @@ -85,6 +51,12 @@ impl From for ExecutionError { } } +impl From for ExecutionError { + fn from(err: LangError) -> Self { + ExecutionError::Lang(err) + } +} + #[derive(Debug)] pub enum HaltReason { Error(ExecutionError), @@ -139,7 +111,7 @@ impl LoopStack { if self.is_loops_empty() { return None; } - return self.ongoing_loops.last(); + self.ongoing_loops.last() } pub fn set_last_loop(&mut self, to: LoopState) { @@ -171,36 +143,36 @@ impl LoopStack { } pub struct Interpreter { - env: EnvId, - root_env: EnvId, - env_man: Shared, + env: Arc, + root_env: Arc, current_program: Option>, // loop_stack: LoopStack, source_processor: SourceProcessor, output: Option>, + // + interner: StringInterner>, } impl Interpreter { pub fn new(out: Option>, with_stdlib: bool) -> Interpreter { - let mut env_man = Environment::new(); + let root_env = Arc::new(EnvironmentFrame::new(None)); + let mut interner = StringInterner::>::new(); if with_stdlib { let native_fns = stdlib(out.clone()); - let env = env_man.top(); for (name, value) in native_fns { - env_man.declare(env, name.to_string(), value); + root_env.define(interner.get_or_intern(name), value); } } - let env = EnvId(0); Interpreter { - env_man: alloc_shared(env_man), - env, - root_env: env, + env: root_env.clone(), + root_env, loop_stack: LoopStack::new(), source_processor: SourceProcessor::new(), current_program: None, output: out, + interner, } } @@ -246,52 +218,44 @@ impl Interpreter { Ok(eval_binary(left_eval, right_eval, operation)) } - fn look_up_variable(&self, name: &str, expr: &Expr) -> Result { - let distance = self.current_program.clone().unwrap().get_distance(expr); + fn look_up_variable(&mut self, name: &str, expr: &Expr) -> Result { + let distance = self.current_program.as_ref().unwrap().get_distance(expr); if let Some(unwrapped) = distance { - self.env_man - .read() - .unwrap() - .read_at(self.env, unwrapped, name) + EnvironmentFrame::read_at( + &self.env, + unwrapped, + name, + &self.interner.get_or_intern(name), + ) } else { - self.env_man.read().unwrap().read(self.root_env, name) + self.root_env.read(name, &self.interner.get_or_intern(name)) } } pub fn user_fn_call( &mut self, statements: &Vec, - closure: EnvId, - parameters: &[String], + closure: Arc, + parameters: &[SymbolU32], arguments: &[RV], ) -> Result { - let fn_env = self.env_man.write().unwrap().push(Some(closure)); + let fn_env = EnvironmentFrame::new(Some(Arc::clone(&closure))); for (i, param) in parameters.iter().enumerate() { // TODO: Remove clone here - self.env_man.write().unwrap().declare( - fn_env, - param.to_string(), - arguments.get(i).unwrap().clone(), - ); + fn_env.define(*param, arguments.get(i).unwrap().clone()); } - self.execute_block(statements, Some(fn_env)) + self.execute_block(statements, Arc::new(fn_env)) } fn execute_block( &mut self, statements: &Vec, - env_opt: Option, + env_opt: Arc, ) -> Result { - let mut env_tmp: Option = None; + let previous = std::mem::replace(&mut self.env, env_opt); - if let Some(env_opt_unwrapped) = env_opt { - env_tmp = Some(self.env); - self.env = env_opt_unwrapped; - } else { - self.env = self.env_man.write().unwrap().push(Some(self.env)); - } let mut ret = Ok(RV::Undefined); for statement in statements { @@ -300,11 +264,9 @@ impl Interpreter { break; } } - if let Some(env_tmp_unwrapped) = env_tmp { - self.env = env_tmp_unwrapped; - } else { - self.env = self.env_man.write().unwrap().remove(self.env); - } + + self.env = previous; + ret } @@ -334,21 +296,8 @@ impl Interpreter { impl VisitorMut for Interpreter { fn visit_expr(&mut self, e: &Expr) -> Result { match e { - Expr::Select { .. } - | Expr::Insert { .. } - | Expr::Update { .. } - | Expr::Delete { .. } => { - let mut planner = Planner::new(self); - let plan = planner.build(e)?; - if let Some(out) = &self.output { - out.write() - .unwrap() - .push(RV::Str(Arc::new(plan.to_string().trim().to_string()))); - } - Ok(RV::Undefined) - } Expr::Literal { value, .. } => Ok(self.literal_to_rv(value)), - Expr::Grouping { expr, .. } => self.visit_expr(expr), + Expr::Variable { name, .. } => self.look_up_variable(&name.name, e), Expr::Unary { operation, expr, .. } => self.eval_unary(operation, expr), @@ -358,29 +307,7 @@ impl VisitorMut for Interpreter { right, .. } => self.eval_binary(left, right, *operation), - Expr::Variable { name, .. } => self.look_up_variable(&name.name, e), - Expr::Assignment { dst, expr, .. } => { - let distance = self.current_program.clone().unwrap().get_distance(e); - let evaluated = self.visit_expr(expr)?; - let result = if let Some(distance_unv) = distance { - self.env_man.write().unwrap().assign_at( - self.env, - distance_unv, - &dst.name, - evaluated.clone(), - ) - } else { - self.env_man.write().unwrap().assign( - self.env, - dst.name.clone(), - evaluated.clone(), - ) - }; - if result.is_err() { - return Err(result.err().unwrap()); - } - Ok(evaluated) - } + Expr::Grouping { expr, .. } => self.visit_expr(expr), Expr::Logical { left, operation, @@ -397,6 +324,29 @@ impl VisitorMut for Interpreter { Ok(RV::Bool(self.visit_expr(right)?.as_bool())) } + Expr::Assignment { dst, expr, .. } => { + let distance = self.current_program.as_ref().unwrap().get_distance(e); + let evaluated = self.visit_expr(expr)?; + let result = if let Some(distance_unv) = distance { + EnvironmentFrame::assign_at( + &self.env, + distance_unv, + &dst.name, + self.interner.get_or_intern(&dst.name), + evaluated.clone(), + ) + } else { + self.root_env.assign( + &dst.name, + self.interner.get_or_intern(&dst.name), + evaluated.clone(), + ) + }; + if result.is_err() { + return Err(result.err().unwrap()); + } + Ok(evaluated) + } Expr::Call { callee, args, span, .. } => { @@ -424,6 +374,7 @@ impl VisitorMut for Interpreter { let val = callable.call(self, args_evaluated.as_slice()); self.loop_stack.pop_fn(); + match val { Err(HaltReason::Return(ret_val)) => Ok(ret_val), Ok(unpacked_val) => Ok(unpacked_val), @@ -450,10 +401,13 @@ impl VisitorMut for Interpreter { "" }; let fun = Function::UserDefined { - name: fn_name.to_string(), + name: self.interner.get_or_intern(fn_name), body: Arc::clone(body), - parameters: parameters.iter().map(|x| x.name.to_string()).collect(), - closure: self.env, + parameters: parameters + .iter() + .map(|x| self.interner.get_or_intern(&x.name)) + .collect(), + closure: self.env.clone(), }; let callable = RV::Callable(Callable::new( @@ -464,9 +418,8 @@ impl VisitorMut for Interpreter { if name.is_some() { // TODO(vck): Callable shouldn't be cloned here - self.env_man.write().unwrap().declare( - self.env, - name.as_ref().unwrap().name.to_string(), + self.env.define( + self.interner.get_or_intern(&name.as_ref().unwrap().name), callable.clone(), ); } @@ -572,6 +525,19 @@ impl VisitorMut for Interpreter { )) } } + Expr::Select { .. } + | Expr::Insert { .. } + | Expr::Update { .. } + | Expr::Delete { .. } => { + let mut planner = Planner::new(self); + let plan = planner.build(e)?; + if let Some(out) = &self.output { + out.write() + .unwrap() + .push(RV::Str(Arc::new(plan.to_string().trim().to_string()))); + } + Ok(RV::Undefined) + } } } @@ -584,21 +550,21 @@ impl VisitorMut for Interpreter { match s { Stmt::Program { body: stmts, .. } => { - return self.execute_block(stmts, Some(self.env)); + return self.execute_block(stmts, self.env.clone()); } Stmt::Expression { expr, .. } => { return self.visit_expr(expr); } Stmt::Declaration { dst, expr, .. } => { let evaluated = self.visit_expr(expr)?; - self.env_man.write().unwrap().declare( - self.env, - dst.name.to_string(), - evaluated.clone(), - ); + self.env + .define(self.interner.get_or_intern(&dst.name), evaluated); } Stmt::Block { body: stmts, .. } => { - return self.execute_block(stmts, None); + return self.execute_block( + stmts, + Arc::new(EnvironmentFrame::new(Some(Arc::clone(&self.env)))), + ); } Stmt::If { condition, diff --git a/lykiadb-server/src/engine/mod.rs b/lykiadb-server/src/engine/mod.rs index e502fb9d..6ee728dd 100644 --- a/lykiadb-server/src/engine/mod.rs +++ b/lykiadb-server/src/engine/mod.rs @@ -1,9 +1,6 @@ use self::error::ExecutionError; use crate::value::RV; use interpreter::Interpreter; -use lykiadb_lang::parser::Parser; -use lykiadb_lang::tokenizer::scanner::Scanner; -use serde_json::Value; use tracing::info; pub mod error; @@ -26,13 +23,6 @@ impl Runtime { Runtime { mode, interpreter } } - pub fn ast(&mut self, source: &str) -> Result { - let tokens = Scanner::scan(source)?; - let program = Parser::parse(&tokens)?; - let json = program.to_json(); - Ok(json) - } - pub fn interpret(&mut self, source: &str) -> Result { let out = self.interpreter.interpret(source); @@ -97,7 +87,7 @@ pub mod test_helpers { .split(',') .map(|flag| { let kv: Vec<&str> = flag.split('=').collect(); - return (kv[0].trim(), kv[1].trim()); + (kv[0].trim(), kv[1].trim()) }) .fold(std::collections::HashMap::new(), |mut acc, (k, v)| { acc.insert(k, v); diff --git a/lykiadb-server/src/plan/mod.rs b/lykiadb-server/src/plan/mod.rs index 4c9fed8f..839e0f81 100644 --- a/lykiadb-server/src/plan/mod.rs +++ b/lykiadb-server/src/plan/mod.rs @@ -1,12 +1,10 @@ use std::fmt::Display; -use lykiadb_lang::{ - ast::{ - expr::Expr, - sql::{ - SqlCollectionIdentifier, SqlCompoundOperator, SqlExpressionSource, SqlJoinType, - SqlOrdering, SqlProjection, - }, +use lykiadb_lang::ast::{ + expr::Expr, + sql::{ + SqlCollectionIdentifier, SqlCompoundOperator, SqlExpressionSource, SqlJoinType, + SqlOrdering, SqlProjection, }, Identifier, Span, }; diff --git a/lykiadb-server/src/plan/planner.rs b/lykiadb-server/src/plan/planner.rs index c42311ce..f9bdcc15 100644 --- a/lykiadb-server/src/plan/planner.rs +++ b/lykiadb-server/src/plan/planner.rs @@ -6,12 +6,10 @@ use crate::{ value::RV, }; -use lykiadb_lang::{ - ast::{ - expr::Expr, - sql::{SqlFrom, SqlJoinType, SqlProjection, SqlSelect, SqlSelectCore, SqlSource}, - visitor::VisitorMut, - }, +use lykiadb_lang::ast::{ + expr::Expr, + sql::{SqlFrom, SqlJoinType, SqlProjection, SqlSelect, SqlSelectCore, SqlSource}, + visitor::VisitorMut, Spanned, }; diff --git a/lykiadb-server/src/plan/scope.rs b/lykiadb-server/src/plan/scope.rs index 42669803..675c83c1 100644 --- a/lykiadb-server/src/plan/scope.rs +++ b/lykiadb-server/src/plan/scope.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use lykiadb_lang::{ast::sql::SqlSource, Identifier}; +use lykiadb_lang::ast::{sql::SqlSource, Identifier}; use super::PlannerError; diff --git a/lykiadb-server/src/value/callable.rs b/lykiadb-server/src/value/callable.rs index d4999d5c..f2c8e61c 100644 --- a/lykiadb-server/src/value/callable.rs +++ b/lykiadb-server/src/value/callable.rs @@ -1,4 +1,4 @@ -use super::environment::EnvId; +use super::environment::EnvironmentFrame; use super::RV; use crate::{ engine::interpreter::{HaltReason, Interpreter}, @@ -7,6 +7,7 @@ use crate::{ use lykiadb_lang::ast::stmt::Stmt; use std::fmt::{Debug, Display, Formatter}; use std::sync::Arc; +use string_interner::symbol::SymbolU32; #[derive(Debug, Clone)] pub enum CallableKind { @@ -39,7 +40,7 @@ impl Callable { closure, body, .. - } => interpreter.user_fn_call(body, *closure, parameters, arguments), + } => interpreter.user_fn_call(body, closure.clone(), parameters, arguments), } } } @@ -55,9 +56,9 @@ pub enum Function { }, Stateful(Shared), UserDefined { - name: String, - parameters: Vec, - closure: EnvId, + name: SymbolU32, + parameters: Vec, + closure: Arc, body: Arc>, }, } @@ -66,7 +67,7 @@ impl Function { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Function::Stateful(_) | Function::Lambda { .. } => write!(f, ""), - Function::UserDefined { name, .. } => write!(f, "{}", name), + Function::UserDefined { .. } => write!(f, ""), } } } diff --git a/lykiadb-server/src/value/environment.rs b/lykiadb-server/src/value/environment.rs index fea828fa..e8934c90 100644 --- a/lykiadb-server/src/value/environment.rs +++ b/lykiadb-server/src/value/environment.rs @@ -1,30 +1,14 @@ use crate::engine::{error::ExecutionError, interpreter::HaltReason}; -use core::panic; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; -use std::borrow::{Borrow, BorrowMut}; +use std::sync::{Arc, RwLock}; +use string_interner::symbol::SymbolU32; use super::RV; - -#[repr(transparent)] -#[derive(Debug, Eq, PartialEq, Clone, Copy)] -pub struct EnvId(pub usize); - -#[derive(Debug)] -struct EnvironmentFrame { - map: FxHashMap, - pub parent: Option, -} - #[derive(Debug)] -pub struct Environment { - envs: Vec, -} - -impl Default for Environment { - fn default() -> Self { - Self::new() - } +pub struct EnvironmentFrame { + map: RwLock>, + pub parent: Option>, } #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] @@ -38,182 +22,187 @@ pub enum EnvironmentError { Other { message: String }, } +macro_rules! to_ancestor { + ($init:expr, $distance:expr) => {{ + let mut env = $init; + for _ in 0..$distance { + env = env.parent.as_ref().unwrap(); + } + env + }}; +} + impl From for ExecutionError { fn from(err: EnvironmentError) -> Self { ExecutionError::Environment(err) } } -impl Environment { - pub fn new() -> Self { - let mut instance = Environment { envs: vec![] }; - instance.push(None); - instance - } - - pub fn push(&mut self, parent: Option) -> EnvId { - self.envs.push(EnvironmentFrame { - map: FxHashMap::default(), +impl EnvironmentFrame { + pub fn new(parent: Option>) -> EnvironmentFrame { + EnvironmentFrame { parent, - }); - EnvId(self.envs.len() - 1) - } - - pub fn remove(&mut self, env_id: EnvId) -> EnvId { - let parent = self.envs[env_id.0].parent.unwrap(); - self.envs.remove(env_id.0); - parent - } - - pub fn top(&self) -> EnvId { - EnvId(self.envs.len() - 1) + map: RwLock::new(FxHashMap::default()), + } } - pub fn declare(&mut self, env_id: EnvId, name: String, value: RV) { - self.envs[env_id.0].map.insert(name, value); + pub fn define(&self, name: SymbolU32, value: RV) { + self.map.write().unwrap().insert(name, value); } - pub fn assign(&mut self, env_id: EnvId, name: String, value: RV) -> Result { - let env = self.envs[env_id.0].borrow(); - if env.map.contains_key(&name) { - self.envs[env_id.0].borrow_mut().map.insert(name, value); + pub fn assign(&self, key: &str, key_sym: SymbolU32, value: RV) -> Result { + if self.map.read().unwrap().contains_key(&key_sym) { + self.map.write().unwrap().insert(key_sym, value); return Ok(true); } - if env.parent.is_some() { - return self.assign(env.parent.unwrap(), name, value); - } - Err(HaltReason::Error( - EnvironmentError::Other { - message: format!("Assignment to an undefined variable '{}'", &name), - } - .into(), - )) + self.parent.as_ref().map_or( + Err(HaltReason::Error( + EnvironmentError::Other { + message: format!("Assignment to an undefined variable '{}'", key), + } + .into(), + )), + |parent| parent.as_ref().assign(key, key_sym, value), + ) } pub fn assign_at( - &mut self, - env_id: EnvId, + env: &Arc, distance: usize, - name: &str, + key: &str, + key_sym: SymbolU32, value: RV, ) -> Result { - let ancestor = self.ancestor(env_id, distance); - - if let Some(unwrapped) = ancestor { - self.envs[unwrapped.0] - .borrow_mut() - .map - .insert(name.to_string(), value); - } else { - self.envs[env_id.0] - .borrow_mut() - .map - .insert(name.to_string(), value); - } - - Ok(true) - } - - pub fn read(&self, env_id: EnvId, name: &str) -> Result { - if self.envs[env_id.0].map.contains_key(name) { - // TODO(vck): Remove clone - return Ok(self.envs[env_id.0].map.get(name).unwrap().clone()); + if distance == 0 { + return env.assign(key, key_sym, value); } - - if self.envs[env_id.0].parent.is_some() { - return self.read(self.envs[env_id.0].parent.unwrap(), name); + if distance == 1 && env.parent.is_some() { + return env.parent.as_ref().unwrap().assign(key, key_sym, value); } - - Err(HaltReason::Error( - EnvironmentError::Other { - message: format!("Variable '{}' was not found", &name), - } - .into(), - )) + to_ancestor!(env, distance).assign(key, key_sym, value) } - pub fn read_at(&self, env_id: EnvId, distance: usize, name: &str) -> Result { - let ancestor = self.ancestor(env_id, distance); - - if let Some(unwrapped) = ancestor { + pub fn read(&self, key: &str, key_sym: &SymbolU32) -> Result { + let guard = self.map.read().unwrap(); + if let Some(value) = guard.get(key_sym) { // TODO(vck): Remove clone - return Ok(self.envs[unwrapped.0].map.get(name).unwrap().clone()); + return Ok(value.clone()); } - if let Some(val) = self.envs[env_id.0].map.get(name) { - return Ok(val.clone()); - } - - Err(HaltReason::Error( - EnvironmentError::Other { - message: format!("Variable '{}' was not found", &name), - } - .into(), - )) - } - - pub fn ancestor(&self, env_id: EnvId, distance: usize) -> Option { + self.parent.as_ref().map_or( + Err(HaltReason::Error( + EnvironmentError::Other { + message: format!("Variable '{}' was not found", key), + } + .into(), + )), + |parent| parent.read(key, key_sym), + ) + } + + pub fn read_at( + env: &Arc, + distance: usize, + key: &str, + key_sym: &SymbolU32, + ) -> Result { if distance == 0 { - return None; + return env.read(key, key_sym); } - if distance == 1 { - return Some(self.envs[env_id.0].parent.unwrap()); + if distance == 1 && env.parent.is_some() { + return env.parent.as_ref().unwrap().read(key, key_sym); } - if self.envs[env_id.0].parent.is_some() { - let pref = self.envs[env_id.0].parent.unwrap(); - return self.ancestor(pref, distance - 1); - } - panic!("Invalid variable distance."); + to_ancestor!(env, distance) + .map + .read() + .unwrap() + .get(key_sym) + .map_or( + Err(HaltReason::Error( + EnvironmentError::Other { + message: format!("Variable '{}' was not found", key), + } + .into(), + )), + |v| Ok(v.clone()), + ) } } #[cfg(test)] mod test { + use std::sync::Arc; + + use string_interner::{backend::StringBackend, symbol::SymbolU32, StringInterner}; + use crate::value::RV; + fn get_interner() -> StringInterner> { + StringInterner::>::new() + } + #[test] fn test_read_basic() { - let mut env_man = super::Environment::new(); - let env = env_man.top(); - env_man.declare(env, "five".to_string(), RV::Num(5.0)); - assert_eq!(env_man.read(env, "five").unwrap(), RV::Num(5.0)); + let env_man = super::EnvironmentFrame::new(None); + let mut interner = get_interner(); + env_man.define(interner.get_or_intern("five"), RV::Num(5.0)); + assert_eq!( + env_man + .read("five", &interner.get_or_intern("five")) + .unwrap(), + RV::Num(5.0) + ); } #[test] fn test_read_from_parent() { - let mut env_man = super::Environment::new(); - let parent = env_man.top(); - env_man.declare(parent, "five".to_string(), RV::Num(5.0)); - let child = env_man.push(Some(parent)); - assert_eq!(env_man.read(child, "five").unwrap(), RV::Num(5.0)); + let root = super::EnvironmentFrame::new(None); + let mut interner = get_interner(); + root.define(interner.get_or_intern("five"), RV::Num(5.0)); + let child = super::EnvironmentFrame::new(Some(Arc::new(root))); + assert_eq!( + child.read("five", &interner.get_or_intern("five")).unwrap(), + RV::Num(5.0) + ); } #[test] fn test_write_to_parent() { - let mut env_man = super::Environment::new(); - let parent = env_man.top(); - env_man.declare(parent, "five".to_string(), RV::Num(5.0)); - let child = env_man.push(Some(parent)); - env_man - .assign(child, "five".to_string(), RV::Num(5.1)) + let root = Arc::new(super::EnvironmentFrame::new(None)); + let mut interner = get_interner(); + + root.define(interner.get_or_intern("five"), RV::Num(5.0)); + + let child = super::EnvironmentFrame::new(Some(root.clone())); + + child + .assign("five", interner.get_or_intern("five"), RV::Num(5.1)) .unwrap(); - assert_eq!(env_man.read(parent, "five").unwrap(), RV::Num(5.1)); - assert_eq!(env_man.read(child, "five").unwrap(), RV::Num(5.1)); + + assert_eq!( + root.read("five", &interner.get_or_intern("five")).unwrap(), + RV::Num(5.1) + ); + + assert_eq!( + child.read("five", &interner.get_or_intern("five")).unwrap(), + RV::Num(5.1) + ); } #[test] fn test_read_undefined_variable() { - let env_man = super::Environment::new(); - let env = env_man.top(); - assert!(env_man.read(env, "five").is_err()); + let env = super::EnvironmentFrame::new(None); + let mut interner = get_interner(); + assert!(env.read("five", &interner.get_or_intern("five")).is_err()); } #[test] fn test_assign_to_undefined_variable() { - let mut env_man = super::Environment::new(); - let env = env_man.top(); - assert!(env_man - .assign(env, "five".to_string(), RV::Num(5.0)) + let env = super::EnvironmentFrame::new(None); + let mut interner = get_interner(); + assert!(env + .assign("five", interner.get_or_intern("five"), RV::Num(5.0)) .is_err()); } } diff --git a/lykiadb-shell/src/main.rs b/lykiadb-shell/src/main.rs index 1decb2f0..cc1302ae 100644 --- a/lykiadb-shell/src/main.rs +++ b/lykiadb-shell/src/main.rs @@ -12,9 +12,6 @@ use lykiadb_connect::{get_session, report_error, Message, Protocol, Request, Res struct Args { /// Path to the script to be executed filename: Option, - - #[clap(short, long, default_value = "false")] - print_ast: bool, } struct Shell; @@ -41,12 +38,7 @@ impl Shell { } } - async fn run_file( - &mut self, - session: &mut impl ClientSession, - filename: &str, - print_ast: bool, - ) { + async fn run_file(&mut self, session: &mut impl ClientSession, filename: &str) { let file = File::open(filename).expect("File couldn't be opened."); let mut content: String = String::new(); @@ -55,11 +47,7 @@ impl Shell { .read_to_string(&mut content) .expect("File couldn't be read."); - let msg = if print_ast { - Message::Request(Request::Ast(content.to_string())) - } else { - Message::Request(Request::Run(content.to_string())) - }; + let msg = Message::Request(Request::Run(content.to_string())); let response = session.send_receive(msg).await.unwrap(); self.handle_response(filename, &content, response); @@ -85,11 +73,7 @@ async fn main() { let mut session = get_session("localhost:19191", Protocol::Tcp).await; let mut shell = Shell; match args.filename { - Some(filename) => { - shell - .run_file(&mut session, &filename, args.print_ast) - .await - } + Some(filename) => shell.run_file(&mut session, &filename).await, None => shell.run_repl(&mut session).await, }; }