diff --git a/examples/fibonacci/fib.tp b/examples/fibonacci/fib.tp new file mode 100644 index 0000000..669f738 --- /dev/null +++ b/examples/fibonacci/fib.tp @@ -0,0 +1,6 @@ +println("Fibonacci sequence:") +let fib = [1,1] + +for i in 2..10: + fib.push(fib[i-2] + fib[i-1]) + println(fib) \ No newline at end of file diff --git a/examples/api/customer/db.tp b/examples/web/api/customer/db.tp similarity index 100% rename from examples/api/customer/db.tp rename to examples/web/api/customer/db.tp diff --git a/examples/api/customer/service.tp b/examples/web/api/customer/service.tp similarity index 100% rename from examples/api/customer/service.tp rename to examples/web/api/customer/service.tp diff --git a/examples/api/customer/web.tp b/examples/web/api/customer/web.tp similarity index 100% rename from examples/api/customer/web.tp rename to examples/web/api/customer/web.tp diff --git a/examples/model/customers.tp b/examples/web/model/customers.tp similarity index 100% rename from examples/model/customers.tp rename to examples/web/model/customers.tp diff --git a/src/builtins/globals.rs b/src/builtins/globals.rs index 5b3017d..85e3b98 100644 --- a/src/builtins/globals.rs +++ b/src/builtins/globals.rs @@ -1,16 +1,17 @@ use crate::builtins::{FunctionMap, Signature, add}; -use crate::compiler::tokens::TokenType::{DateTime, Void}; +use crate::compiler::tokens::TokenType::{DateTime, StringType, Void}; use crate::errors::RuntimeError; use crate::value::Value; use std::collections::HashMap; use std::sync::LazyLock; +use crate::compiler::ast_pass::Parameter; pub(crate) static GLOBAL_FUNCTIONS: LazyLock = LazyLock::new(|| { let mut global_functions: FunctionMap = HashMap::new(); let functions = &mut global_functions; add(functions, "now", Signature::new(vec![], DateTime, now)); - add(functions, "print", Signature::new(vec![], Void, print)); - add(functions, "println", Signature::new(vec![], Void, println)); + add(functions, "print", Signature::new(vec![Parameter::new("text", StringType)], Void, print)); + add(functions, "println", Signature::new(vec![Parameter::new("text", StringType)], Void, println)); global_functions }); diff --git a/src/compiler/assembly_pass.rs b/src/compiler/assembly_pass.rs index a620077..758898b 100644 --- a/src/compiler/assembly_pass.rs +++ b/src/compiler/assembly_pass.rs @@ -285,6 +285,7 @@ impl AsmPass { // maybe global function _ => { if let Some(fun) = GLOBAL_FUNCTIONS.get(name) { + self.get_arguments_in_order(namespace, symbols, registry, arguments, &fun.parameters)?; self.emit(Call(name_index, fun.arity())); } else { return Err( diff --git a/src/compiler/mod.rs b/src/compiler/mod.rs index 3b784ac..2472755 100644 --- a/src/compiler/mod.rs +++ b/src/compiler/mod.rs @@ -55,8 +55,7 @@ pub fn compile(src: &str) -> Result, TipiLangError> { Ok(asm_registry) } -#[cfg(test)] -pub(crate) fn run(src: &str) -> Result { +pub fn run(src: &str) -> Result { let tokens = scan_pass::scan(src)?; let mut symbol_table = HashMap::new(); let ast = ast_pass::compile(None, tokens, &mut symbol_table)?; diff --git a/src/main.rs b/src/main.rs index e59d858..4afdb75 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,17 @@ +use arc_swap::ArcSwap; use axum::extract::{Request, State}; use axum::http::StatusCode; use axum::routing::any; use axum::{Json, Router}; use clap::Parser; +use log::info; +use std::collections::HashMap; +use std::fs; +use std::sync::Arc; +use tipi_lang::compiler::assembly_pass::AsmChunk; +use tipi_lang::compiler::{compile, compile_sourcedir, map_underlying, run}; use tipi_lang::errors::TipiLangError; use tipi_lang::vm::interpret_async; -use std::collections::HashMap; -use std::sync::Arc; -use arc_swap::ArcSwap; -use log::info; -use tipi_lang::compiler::assembly_pass::AsmChunk; -use tipi_lang::compiler::{compile_sourcedir, map_underlying}; /// A simple CLI tool to greet users #[derive(Parser, Debug)] @@ -23,48 +24,56 @@ struct Args { #[arg(short, long)] watch: bool, + + #[arg(short, long)] + file: Option, } #[tokio::main] async fn main() -> Result<(), TipiLangError> { - println!("-- Tipilang --"); tracing_subscriber::fmt::init(); let args = Args::parse(); - let source = args.source.unwrap_or("./source".to_string()); - let registry = compile_sourcedir(&source)?; - let empty = registry.is_empty(); - - let swap = Arc::new(ArcSwap::from(Arc::new(registry))); - if !empty { - if args.watch { - tipi_lang::file_watch::start_watch_daemon(&source, swap.clone()); - } - println!("-- Compilation successful --"); - let state =AppState { - registry: swap.clone(), - }; - let app = Router::new() - .route("/", any(handle_any).with_state(state.clone())) - .route("/{*path}", any(handle_any).with_state(state.clone())); - - let listener = tokio::net::TcpListener::bind("0.0.0.0:3000") - .await - .map_err(map_underlying())?; - - println!( - "-- Listening on {} --\n", - listener.local_addr().map_err(map_underlying())? - ); - - if args.repl { - let _ = std::thread::spawn(move || tipi_lang::repl::start(swap.clone())); - } - - axum::serve(listener, app).await.map_err(map_underlying())?; + if let Some(file) = args.file { + let source = fs::read_to_string(file).expect("Unable to read file"); + run(&source)?; } else { - println!("No source files found or compilation error"); - if args.repl { - tipi_lang::repl::start(swap.clone())?; + println!("-- Tipilang --"); + let source = args.source.unwrap_or("./source".to_string()); + let registry = compile_sourcedir(&source)?; + let empty = registry.is_empty(); + + let swap = Arc::new(ArcSwap::from(Arc::new(registry))); + if !empty { + if args.watch { + tipi_lang::file_watch::start_watch_daemon(&source, swap.clone()); + } + println!("-- Compilation successful --"); + let state = AppState { + registry: swap.clone(), + }; + let app = Router::new() + .route("/", any(handle_any).with_state(state.clone())) + .route("/{*path}", any(handle_any).with_state(state.clone())); + + let listener = tokio::net::TcpListener::bind("0.0.0.0:3000") + .await + .map_err(map_underlying())?; + + println!( + "-- Listening on {} --\n", + listener.local_addr().map_err(map_underlying())? + ); + + if args.repl { + let _ = std::thread::spawn(move || tipi_lang::repl::start(swap.clone())); + } + + axum::serve(listener, app).await.map_err(map_underlying())?; + } else { + println!("No source files found or compilation error"); + if args.repl { + tipi_lang::repl::start(swap.clone())?; + } } } Ok(()) @@ -99,7 +108,7 @@ async fn handle_any( headers.insert(k.to_string(), v.to_str().unwrap().to_string()); } let path = &req.uri().to_string(); - info!("invoked {:?} => {}",req, function_qname); + info!("invoked {:?} => {}", req, function_qname); match interpret_async( state.registry.load(), &function_qname, @@ -112,7 +121,12 @@ async fn handle_any( Ok(value) => Ok(Json(value.to_string())), Err(_) => { // url checks out but function for method not found - if state.registry.load().get(&format!("{}.main", component)).is_some() { + if state + .registry + .load() + .get(&format!("{}.main", component)) + .is_some() + { Err(StatusCode::METHOD_NOT_ALLOWED) } else { Err(StatusCode::NOT_FOUND) diff --git a/src/vm.rs b/src/vm.rs index 6de8641..de4b553 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -156,10 +156,14 @@ impl Vm { self.push(value); } Op::ListGet => { - let index = self.pop(); + let index = self.pop().cast_usize()?; let list = self.pop(); if let Value::List(list) = list { - self.push(list.get(index.cast_usize()?).cloned().unwrap()) + if list.len() <= index { + return Err(RuntimeError::IndexOutOfBounds(list.len(), index)); + } else { + self.push(list.get(index).cloned().unwrap()) + } } } Op::CallBuiltin(function_name_index, function_type_index, num_args) => { @@ -252,7 +256,9 @@ impl Vm { } fn push(&mut self, value: Value) { - self.stack.push(value); + if value != Value::Void { + self.stack.push(value); + } } fn pop(&mut self) -> Value {