diff --git a/examples/web/api/customer/service.tp b/examples/web/api/customer/service.tp index a08ffd0..a8121c5 100644 --- a/examples/web/api/customer/service.tp +++ b/examples/web/api/customer/service.tp @@ -1,2 +1,2 @@ -fn get_all() -> list: - db::get_all() +fn get_all() -> string: + db::get_all() \ No newline at end of file diff --git a/examples/web/api/customer/web.tp b/examples/web/api/customer/web.tp index fc64299..819fcdc 100644 --- a/examples/web/api/customer/web.tp +++ b/examples/web/api/customer/web.tp @@ -1,9 +1,2 @@ fn get() -> list: - service::get_all() - -fn post(customer: Customer): - service.add(customer) - - -fn put(customer: Customer): - service.update(customer) + service::get_all() \ No newline at end of file diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs index a08a497..dc0c585 100644 --- a/src/builtins/mod.rs +++ b/src/builtins/mod.rs @@ -39,7 +39,7 @@ impl Signature { } pub(crate) fn has_varargs(&self) -> bool { - self.parameters.last().unwrap().name.lexeme == "varargs" + !self.parameters.is_empty() && self.parameters.last().unwrap().name.lexeme == "varargs" } } diff --git a/src/compiler/assembly_pass.rs b/src/compiler/assembly_pass.rs index 4d0400d..785c545 100644 --- a/src/compiler/assembly_pass.rs +++ b/src/compiler/assembly_pass.rs @@ -16,6 +16,7 @@ use crate::value::Value; use crate::{AsmRegistry, SymbolTable}; use std::collections::HashMap; use std::ops::Deref; +use crate::compiler::namespace::Namespace; pub fn compile( qualified_name: Option<&str>, @@ -30,7 +31,7 @@ pub fn compile_function( function: &Function, symbols: &SymbolTable, registry: &mut AsmRegistry, - namespace: &str, + namespace: &Namespace, ) -> Result { let fn_name = &function.name.lexeme; let mut compiler = AsmPass::new(fn_name); @@ -53,7 +54,7 @@ pub fn compile_in_namespace( ) -> Result<(), CompilerErrorAtLine> { let name = namespace.unwrap_or("main"); let mut compiler = AsmPass::new(name); - let chunk = compiler.compile(ast, symbols, registry, name)?; + let chunk = compiler.compile(ast, symbols, registry, &Namespace::new(name))?; registry.insert(name.to_string(), chunk); Ok(()) } @@ -136,7 +137,7 @@ impl AsmPass { ast: &Vec, symbols: &SymbolTable, registry: &mut AsmRegistry, - namespace: &str, + namespace: &Namespace, ) -> Result { self.compile_statements(ast, symbols, registry, namespace)?; self.emit(Return); @@ -151,7 +152,7 @@ impl AsmPass { ast: &Vec, symbols: &SymbolTable, registry: &mut AsmRegistry, - namespace: &str, + namespace: &Namespace, ) -> Result<(), CompilerErrorAtLine> { for statement in ast { self.compile_statement(statement, symbols, registry, namespace)?; @@ -165,7 +166,7 @@ impl AsmPass { statement: &Statement, symbols: &SymbolTable, registry: &mut AsmRegistry, - namespace: &str, + namespace: &Namespace, ) -> Result<(), CompilerErrorAtLine> { self.current_line = statement.line(); match statement { @@ -193,7 +194,7 @@ impl AsmPass { fn compile_expression( &mut self, - namespace: &str, + namespace: &Namespace, expression: &Expression, symbols: &SymbolTable, registry: &mut AsmRegistry, @@ -258,8 +259,8 @@ impl AsmPass { Expression::FunctionCall { name, arguments, .. } => { - let qname = format!("{}/{}", namespace, name); - let function = symbols.get(&qname).or(symbols.get(name)); + let qname = format!("{}/{}", namespace.pop(), name.replace("::","/")); + let function = symbols.get(&qname); match function { Some(Symbol::Function { parameters, .. }) => { let name_index = self.chunk.find_constant(&qname).unwrap_or_else(|| { @@ -496,7 +497,7 @@ impl AsmPass { // named parameters do not have to be passed in order, but they do need to be evaluated in the order of the called function/constructor fn get_arguments_in_order( &mut self, - namespace: &str, + namespace: &Namespace, symbols: &SymbolTable, registry: &mut AsmRegistry, arguments: &[Expression], diff --git a/src/compiler/ast_pass.rs b/src/compiler/ast_pass.rs index 07bde5c..b47aa4c 100644 --- a/src/compiler/ast_pass.rs +++ b/src/compiler/ast_pass.rs @@ -1,8 +1,4 @@ use crate::builtins::globals::GLOBAL_FUNCTIONS; -use crate::compiler::ast_pass::Expression::{ - Assignment, FieldGet, FunctionCall, IfElseExpression, IfExpression, LetExpression, ListGet, - MapGet, MethodCall, NamedParameter, Stop, Variable, -}; use crate::compiler::tokens::TokenType::{ Bang, Bool, Char, Colon, DateTime, Dot, Else, Eof, Eol, Equal, False, FloatingPoint, Fn, For, Greater, GreaterEqual, GreaterGreater, Identifier, If, In, Indent, Integer, LeftBrace, @@ -151,7 +147,7 @@ impl AstCompiler { Err(self.error_at_line(Expected("-> or ?"))) }; } - Ok(Stop { + Ok(Expression::Stop { line: self.peek().line, }) } @@ -162,7 +158,7 @@ impl AstCompiler { self.consume(&RightBrace, Expected("'}' after guard expression."))?; Ok(query_params) } else { - Ok(Stop { + Ok(Expression::Stop { line: self.peek().line, }) } @@ -174,7 +170,7 @@ impl AstCompiler { self.consume(&RightBrace, Expected("'}' after guard expression."))?; Ok(path_params) } else { - Ok(Stop { + Ok(Expression::Stop { line: self.peek().line, }) } @@ -347,7 +343,7 @@ impl AstCompiler { }, ); - Ok(LetExpression { + Ok(Expression::LetExpression { name: name_token, var_type, initializer: Box::new(initializer), @@ -391,8 +387,8 @@ impl AstCompiler { if self.match_token(&[Equal]) { let operator = self.previous().clone(); let right = self.comparison(symbol_table)?; - if let Variable { name, .. } = expr { - Ok(Assignment { + if let Expression::Variable { name, .. } = expr { + Ok(Expression::Assignment { line: operator.line, variable_name: name.to_string(), value: Box::new(right), @@ -499,13 +495,13 @@ impl AstCompiler { self.inc_indent(); - Ok(IfElseExpression { + Ok(Expression::IfElseExpression { condition: Box::new(condition), then_branch, else_branch: self.compile(symbol_table)?, }) } else { - Ok(IfExpression { + Ok(Expression::IfExpression { condition: Box::new(condition), then_branch, }) @@ -534,22 +530,23 @@ impl AstCompiler { } } + // index into list fn index(&mut self, operand: Expression, index: Expression) -> Expr { let get = match &operand { - Expression::Map { .. } => MapGet { + Expression::Map { .. } => Expression::MapGet { map: Box::new(operand), key: Box::new(index), }, - Expression::List { .. } => ListGet { + Expression::List { .. } => Expression::ListGet { list: Box::new(operand), index: Box::new(index), }, - Variable { var_type, .. } => match var_type { - ListType => ListGet { + Expression::Variable { var_type, .. } => match var_type { + ListType => Expression::ListGet { list: Box::new(operand), index: Box::new(index), }, - MapType => MapGet { + MapType => Expression::MapGet { map: Box::new(operand), key: Box::new(index), }, @@ -578,7 +575,7 @@ impl AstCompiler { ) -> Expr { if self.match_token(&[LeftParen]) { let arguments = self.arguments(symbol_table)?; - Ok(MethodCall { + Ok(Expression::MethodCall { receiver: Box::new(receiver.clone()), method_name: op.lexeme, arguments, @@ -586,7 +583,7 @@ impl AstCompiler { }) } else { // no test yet - Ok(FieldGet { + Ok(Expression::FieldGet { receiver: Box::new(receiver.clone()), field: op.lexeme.clone(), }) @@ -698,7 +695,7 @@ impl AstCompiler { fn named_parameter(&mut self, name: &Token, symbol_table: &mut SymbolTable) -> Expr { let value = self.expression(symbol_table)?; let line = name.line; - Ok(NamedParameter { + Ok(Expression::NamedParameter { name: name.clone(), value: Box::new(value), line, @@ -751,7 +748,7 @@ impl AstCompiler { } else { &Unknown }; - Ok(Variable { + Ok(Expression::Variable { name: name.lexeme.to_string(), var_type: var_type.clone(), line: name.line, @@ -760,7 +757,7 @@ impl AstCompiler { fn function_call(&mut self, name: Token, symbol_table: &mut SymbolTable) -> Expr { let arguments = self.arguments(symbol_table)?; - Ok(FunctionCall { + Ok(Expression::FunctionCall { line: self.peek().line, name: name.lexeme.to_string(), arguments, @@ -1020,19 +1017,19 @@ impl Expression { Self::Range { line, .. } => *line, Self::List { line, .. } => *line, Self::Map { line, .. } => *line, - Variable { line, .. } => *line, - Assignment { line, .. } => *line, - FunctionCall { line, .. } => *line, - MethodCall { line, .. } => *line, - Stop { line } => *line, - NamedParameter { line, .. } => *line, - MapGet { .. } => 0, - ListGet { .. } => 0, - FieldGet { .. } => 0, - IfExpression { condition, .. } => condition.line(), - IfElseExpression { condition, .. } => condition.line(), - LetExpression { name, .. } => name.line, - Expression::ForStatement { loop_var, .. } => loop_var.line, + Self::Variable { line, .. } => *line, + Self::Assignment { line, .. } => *line, + Self::FunctionCall { line, .. } => *line, + Self::MethodCall { line, .. } => *line, + Self::Stop { line } => *line, + Self::NamedParameter { line, .. } => *line, + Self::MapGet { .. } => 0, + Self::ListGet { .. } => 0, + Self::FieldGet { .. } => 0, + Self::IfExpression { condition, .. } => condition.line(), + Self::IfElseExpression { condition, .. } => condition.line(), + Self::LetExpression { name, .. } => name.line, + Self::ForStatement { loop_var, .. } => loop_var.line, } } } diff --git a/src/compiler/compiler_tests.rs b/src/compiler/compiler_tests.rs index 265c243..8d6314c 100644 --- a/src/compiler/compiler_tests.rs +++ b/src/compiler/compiler_tests.rs @@ -2,7 +2,7 @@ mod tests { use crate::DATE_FORMAT_TIMEZONE; use crate::compiler::{compile, run}; - use crate::errors::CompilerError::{IllegalArgumentsException, ReservedFunctionName}; + use crate::errors::CompilerError::{IllegalArgumentsException, ReservedFunctionName, UndeclaredVariable}; use crate::errors::CompilerErrorAtLine; use crate::errors::RuntimeError::{IllegalArgumentException, IndexOutOfBounds, NotSortable}; use crate::errors::TipiLangError::{Compiler, Runtime}; @@ -474,20 +474,21 @@ else: a"#) .unwrap(); } - // #[test] - // fn package() { - // assert_eq!(run(r#"a::b.c()"#), Ok(Value::U32(48))); - // } #[test] - fn guards() { - assert_eq!( - run(r#"fn get_all_users() -> list: - | /{uuid} -> service.get_by_uuid(uuid)?"#), - Ok(Value::Void) - ); + fn package() { + assert_eq!(run(r#"a::b.c()"#), Err(Compiler(CompilerErrorAtLine {error: UndeclaredVariable("a::b".to_string()), line: 1 }))); } + // #[test] + // fn guards() { + // assert_eq!( + // run(r#"fn get_all_users() -> list: + // | /{uuid} -> service.get_by_uuid(uuid)?"#), + // Ok(Value::Void) + // ); + // } + #[test] fn query() { let result = diff --git a/src/compiler/mod.rs b/src/compiler/mod.rs index 9c9ec14..bbf31f7 100644 --- a/src/compiler/mod.rs +++ b/src/compiler/mod.rs @@ -11,9 +11,11 @@ pub mod scan_pass; pub mod ast_pass; pub mod tokens; pub mod assembly_pass; +pub(crate) mod namespace; pub fn compile_sourcedir(source_dir: &str) -> Result, TipiLangError> { let mut asm_registry = AsmRegistry::new(); + let mut symbol_table = HashMap::new(); for entry in WalkDir::new(source_dir).into_iter().filter_map(|e| e.ok()) { let path = entry.path().to_str().unwrap(); @@ -21,7 +23,6 @@ pub fn compile_sourcedir(source_dir: &str) -> Result, println!("-- Compiling {} -- ", path); let source = fs::read_to_string(path).map_err(map_underlying())?; let tokens = scan_pass::scan(&source)?; - let mut symbol_table = HashMap::new(); match ast_pass::compile(Some(path), tokens, &mut symbol_table) { Ok(statements) => { let path = path.strip_prefix(source_dir).unwrap().replace(TIPI_EXT, ""); diff --git a/src/compiler/namespace.rs b/src/compiler/namespace.rs new file mode 100644 index 0000000..af7f977 --- /dev/null +++ b/src/compiler/namespace.rs @@ -0,0 +1,27 @@ +use std::fmt::Display; + +pub(crate) struct Namespace { + elements: Vec, +} + +impl Namespace { + pub(crate) fn new(path: &str) -> Self { + Self { + elements: path.split('/').map(|path| path.to_string()).collect(), + } + } + + pub(crate) fn pop(&self) -> Self{ + let mut copy = self.elements.to_vec(); + copy.pop(); + Self{ + elements: copy, + } + } +} + +impl Display for Namespace { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.elements.join("/")) + } +} diff --git a/src/compiler/scan_pass.rs b/src/compiler/scan_pass.rs index c822f2e..875abaf 100644 --- a/src/compiler/scan_pass.rs +++ b/src/compiler/scan_pass.rs @@ -88,12 +88,9 @@ impl Scanner { '#' => self.add_token(TokenType::Hash), '+' => self.add_token(TokenType::Plus), ':' => { - let t = if self.match_next(':') { - TokenType::ColonColon - } else { - TokenType::Colon - }; - self.add_token(t); + if !self.match_next(':') { + self.add_token(TokenType::Colon); + } } ';' => println!("Warning: Ignoring semicolon at line {}", self.line), '*' => self.add_token(TokenType::Star), @@ -189,7 +186,13 @@ impl Scanner { } fn identifier(&mut self) { - while is_alphanumeric(self.peek()) { + while is_alphanumeric(self.peek()) + || self.peek() == '_' + || (self.peek() == ':' && self.peek_next() == ':') + { + if self.peek() == ':' && self.peek_next() == ':' { + self.advance(); + } self.advance(); } let value: String = self.chars[self.start..self.current].iter().collect(); diff --git a/src/repl.rs b/src/repl.rs index bc901f3..7a01da6 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -10,6 +10,7 @@ use std::io; use std::io::Write; use std::ops::Deref; use std::sync::Arc; +use crate::compiler::namespace::Namespace; pub fn start(registry: Arc>>) -> Result<(), TipiLangError> { println!("REPL started -- Type ctrl-c to exit (both the repl and the server)"); @@ -44,7 +45,7 @@ pub fn start(registry: Arc>>) -> Result<(), Ti }; symbol_builder::build("", &ast, &mut symbol_table); - match asm_pass.compile(&ast, &symbol_table, &mut registry_copy, "") { + match asm_pass.compile(&ast, &symbol_table, &mut registry_copy, &Namespace::new("")) { Ok(chunk) => { registry_copy.insert("main".to_string(), chunk); registry.store(Arc::new(registry_copy)); diff --git a/src/symbol_builder.rs b/src/symbol_builder.rs index 8d9c0b2..ba7b45a 100644 --- a/src/symbol_builder.rs +++ b/src/symbol_builder.rs @@ -33,7 +33,7 @@ fn make_qname(path: &str, name: &Token) -> String { if path.is_empty() { name.lexeme.to_string() } else { - format!("{}.{}", path, name.lexeme) + format!("{}/{}", path, name.lexeme) } } diff --git a/syntax/tipi/package.json b/syntax/tipi/package.json index ac7dc17..c4a7db9 100644 --- a/syntax/tipi/package.json +++ b/syntax/tipi/package.json @@ -13,7 +13,7 @@ "languages": [{ "id": "tipi", "aliases": ["tipi-lang", "tipi"], - "extensions": [".tipi"], + "extensions": [".tp"], "configuration": "./language-configuration.json" }], "grammars": [{