From 954f817437acd7792bda911f823ee4b413d7df12 Mon Sep 17 00:00:00 2001 From: TudbuT Date: Mon, 6 Mar 2023 14:56:49 +0100 Subject: [PATCH] more http progress --- http.spl | 53 +++++++++++++++++- iter.spl | 2 +- spl.vim | 2 +- src/dyn_fns.rs | 54 +++++++++++------- src/lexer.rs | 17 ++++++ src/lib.rs | 8 +-- src/runtime.rs | 147 ++++++++++++++++++++++++++++++++++++++++++------- src/std_fns.rs | 39 ++++--------- std.spl | 41 +++++++++++++- test.spl | 8 ++- 10 files changed, 287 insertions(+), 84 deletions(-) diff --git a/http.spl b/http.spl index f30df29..eefe218 100644 --- a/http.spl +++ b/http.spl @@ -6,6 +6,7 @@ construct net:http namespace { Request Response + help } construct net:http:Request { @@ -58,22 +59,68 @@ construct net:http:Request { body stream:write-exact; stream:flush; - 1024 stream:read-to-end:to-str println + def response 1024 stream:read-to-end =response + + response net:http:Response:new:read-from-bytes stream:close; + } +} - "todo" panic +construct net:http:help namespace { + ; + assert-str { | with expected iter _ ; + [ { | pop iter:next } (expected _array):len:foreach ] _str + expected _str + eq not if { + "Expected " expected concat throw + } + } + until-str { str | with expected iter _ ; + def match 0 =match + def bytes expected:to-bytes =bytes + [ + while { match bytes:len eq not } { + iter:next dup (match bytes:get) eq dup if { + match ++ =match + } not if { + 0 =match + } + } + { | pop pop } match:foreach + ] _str } } construct net:http:Response { + version state-num state-msg headers body ; construct { this | with this ; - List:new this:=headers + MicroMap:new this:=headers "" this:=body this } + read-from-bytes { this | with bytes this ; + use net:http:help + bytes:iter =bytes + "HTTP/" bytes help:assert-str + " " bytes help:until-str this:=version + " " bytes help:until-str _mega this:=state-num + "\r\n" bytes help:until-str this:=state-msg + while { "\r\n" bytes help:until-str dup "" eq not } { + def iter ": " swap:split:iter =iter + ( + iter:next ": " + iter:join + ) this:headers:set; + } pop + 0 ("Content-Length" this:headers:get _mega) bytes:collect:sub this:=body + this + } + content-type { str | with this ; + "Content-Type" this:headers:get + } } diff --git a/iter.spl b/iter.spl index e1e8b8a..498749f 100644 --- a/iter.spl +++ b/iter.spl @@ -31,7 +31,7 @@ construct _Iter { } join { str | with separator this ; { str | with accum item ; - accum _str separator item _str strconcat strconcat + accum _str separator item _str concat concat } this:reduce:calculate } filter { FilterIter | with filter this ; diff --git a/spl.vim b/spl.vim index 5dbf8a2..5cc44af 100644 --- a/spl.vim +++ b/spl.vim @@ -6,7 +6,7 @@ endif syn match Comment /".*?";/ syn match Number /\<[0-9._]*\>/ syn match Function /\/ diff --git a/src/dyn_fns.rs b/src/dyn_fns.rs index 7168d7b..300008f 100644 --- a/src/dyn_fns.rs +++ b/src/dyn_fns.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use crate::{lexer, runtime::*}; +use crate::*; pub fn dyn_dump(stack: &mut Stack) -> OError { Words { @@ -76,11 +77,8 @@ pub fn dyn_def_field(stack: &mut Stack) -> OError { ) else { return stack.err(ErrorKind::InvalidCall("dyn-def-field".to_owned())) }; - runtime(|rt| rt.get_type_by_name(s.to_owned())) - .ok_or_else(|| Error { - kind: ErrorKind::TypeNotFound(s), - stack: stack.trace(), - })? + runtime(|rt| rt.get_type_by_name(&s)) + .ok_or_else(|| stack.error(ErrorKind::TypeNotFound(s)))? .lock() .add_property(name, stack.get_frame())?; Ok(()) @@ -98,11 +96,8 @@ pub fn dyn_def_method(stack: &mut Stack) -> OError { ) else { return stack.err(ErrorKind::InvalidCall("dyn-def-method".to_owned())) }; - runtime(|rt| rt.get_type_by_name(s.to_owned())) - .ok_or_else(|| Error { - kind: ErrorKind::TypeNotFound(s), - stack: stack.trace(), - })? + runtime(|rt| rt.get_type_by_name(&s)) + .ok_or_else(|| stack.error(ErrorKind::TypeNotFound(s)))? .lock() .functions .insert(name, f); @@ -214,10 +209,9 @@ pub fn dyn_read(stack: &mut Stack) -> OError { stack.push( Value::Func(AFunc::new(Func { ret_count: 0, - to_call: FuncImpl::SPL(lexer::lex(s).map_err(|x| Error { - kind: ErrorKind::LexError(format!("{x:?}")), - stack: stack.trace(), - })?), + to_call: FuncImpl::SPL( + lexer::lex(s).map_err(|x| stack.error(ErrorKind::LexError(format!("{x:?}"))))?, + ), run_as_base: false, origin: stack.get_frame(), fname: None, @@ -241,10 +235,9 @@ pub fn dyn_readf(stack: &mut Stack) -> OError { stack.push( Value::Func(AFunc::new(Func { ret_count: 0, - to_call: FuncImpl::SPL(lexer::lex(s).map_err(|x| Error { - kind: ErrorKind::LexError(format!("{x:?}")), - stack: stack.trace(), - })?), + to_call: FuncImpl::SPL( + lexer::lex(s).map_err(|x| stack.error(ErrorKind::LexError(format!("{x:?}"))))?, + ), run_as_base: true, origin: stack.get_frame(), fname: Some(n), @@ -255,6 +248,27 @@ pub fn dyn_readf(stack: &mut Stack) -> OError { Ok(()) } +pub fn dyn_catch(stack: &mut Stack) -> OError { + require_on_stack!(ctch, Func, stack, "dyn-catch"); + require_on_stack!(blk, Func, stack, "dyn-catch"); + require_on_stack!(types, Array, stack, "dyn-catch"); + if let Err(e) = blk.to_call.call(stack) { + if types.is_empty() || types.contains(&e.kind.to_string().spl()) { + stack.push(e.spl()); + ctch.to_call.call(stack) + } else { + Err(e) + } + } else { + Ok(()) + } +} + +pub fn dyn_use(stack: &mut Stack) -> OError { + require_on_stack!(item, Str, stack, "dyn-use"); + Words::new(vec![Word::Key(Keyword::Use(item))]).exec(stack) +} + pub(crate) fn wrap(f: fn(&mut Stack) -> OError) -> impl Fn(&mut Stack) -> OError { move |stack| unsafe { let frame = stack.pop_frame(0); @@ -266,7 +280,7 @@ pub(crate) fn wrap(f: fn(&mut Stack) -> OError) -> impl Fn(&mut Stack) -> OError pub fn register(r: &mut Stack, o: Arc) { type Fn = fn(&mut Stack) -> OError; - let fns: [(&str, Fn, u32); 15] = [ + let fns: [(&str, Fn, u32); 17] = [ ("dyn-__dump", dyn_dump, 0), ("dyn-def", dyn_def, 0), ("dyn-func", dyn_func, 0), @@ -282,6 +296,8 @@ pub fn register(r: &mut Stack, o: Arc) { ("dyn-all-types", dyn_all_types, 1), ("dyn-read", dyn_read, 1), ("dyn-readf", dyn_readf, 1), + ("dyn-catch", dyn_catch, 0), + ("dyn-use", dyn_use, 0), ]; for f in fns { r.define_func( diff --git a/src/lexer.rs b/src/lexer.rs index dfc3006..87a0bbe 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -121,6 +121,11 @@ fn read_block(str_words: &[String], isfn: bool) -> Result<(Option, Words, u } i += 3; } + "use" => { + let item = str_words[i + 1].to_owned(); + i += 1; + words.push(Word::Key(Keyword::Use(item))); + } "while" => { let cond = read_block(&str_words[i + 2..], false)?; i += 2 + cond.2; @@ -133,6 +138,18 @@ fn read_block(str_words: &[String], isfn: bool) -> Result<(Option, Words, u i += 2 + blk.2; words.push(Word::Key(Keyword::If(blk.1))); } + "catch" => { + let mut types = Vec::new(); + i += 1; + while &str_words[i] != "{" { + types.push(str_words[i].to_owned()); + i += 1; + } + let blk = read_block(&str_words[i..], false)?; + i += 2 + blk.2; + let ctch = read_block(&str_words[i..], false)?; + words.push(Word::Key(Keyword::Catch(types, blk.1, ctch.1))) + } "with" => { let mut vars = Vec::new(); i += 1; diff --git a/src/lib.rs b/src/lib.rs index 3062ca2..54a26b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,7 +63,7 @@ pub fn start_file_in_runtime(path: &str) -> Result { Ok(stack) } -/// Inćlude the standard library in a runtime-stack-pair, where the runtime has been .set(). +/// Include the standard library in a runtime-stack-pair, where the runtime has been .set(). pub fn add_std(stack: &mut Stack) -> OError { let f = find_in_splpath("std.spl"); let words = lex(if let Ok(f) = f { @@ -71,10 +71,7 @@ pub fn add_std(stack: &mut Stack) -> OError { } else { f.unwrap_err() }) - .map_err(|x| Error { - kind: ErrorKind::LexError(format!("{x:?}")), - stack: Vec::new(), - })?; + .map_err(|x| stack.error(ErrorKind::LexError(format!("{x:?}"))))?; words.exec(stack) } @@ -94,6 +91,7 @@ nofmt! { }; }; } + #[macro_export] macro_rules! require_int_on_stack { ($name:tt, $stack:expr, $fn:literal) => { diff --git a/src/runtime.rs b/src/runtime.rs index cb35182..8aa66a7 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -46,6 +46,10 @@ pub fn runtime_mut(f: impl FnOnce(RwLockWriteGuard) -> T) -> T { }) } +pub fn get_type(name: &str) -> Option { + runtime(|rt| rt.get_type_by_name(name)) +} + /// An SPL runtime. /// /// This holds: @@ -99,8 +103,8 @@ impl Runtime { rt } - pub fn get_type_by_name(&self, name: String) -> Option { - self.types_by_name.get(&name).cloned() + pub fn get_type_by_name(&self, name: &str) -> Option { + self.types_by_name.get(name).cloned() } pub fn get_type_by_id(&self, id: u32) -> Option { @@ -166,7 +170,7 @@ impl SetRuntime for Arc> { } /// A frame's location in SPL code. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct FrameInfo { pub file: String, pub function: String, @@ -288,10 +292,7 @@ impl Frame { if let Some(ref x) = frame.parent { frame = x; } else { - return Err(Error { - kind: ErrorKind::VariableNotFound(name), - stack: stack.trace(), - }); + return Err(stack.error(ErrorKind::VariableNotFound(name))); } } } @@ -305,10 +306,7 @@ impl Frame { if let Some(ref x) = frame.parent { frame = x; } else { - return Err(Error { - kind: ErrorKind::VariableNotFound(name), - stack: stack.trace(), - }); + return Err(stack.error(ErrorKind::VariableNotFound(name))); } } } @@ -457,10 +455,7 @@ impl Stack { if let Some(ref x) = frame.parent { frame = x; } else { - return Err(Error { - kind: ErrorKind::FuncNotFound(name), - stack: self.trace(), - }); + return Err(self.error(ErrorKind::FuncNotFound(name))); } } } @@ -553,6 +548,7 @@ impl Stack { Err(Error { kind, stack: self.trace(), + mr_stack: self.mr_trace(), }) } @@ -560,6 +556,7 @@ impl Stack { Error { kind, stack: self.trace(), + mr_stack: self.mr_trace(), } } @@ -620,7 +617,7 @@ pub enum Keyword { /// def /// /// Defines a variable. - /// equivalent to dyn-def + /// equivalent to "" dyn-def Def(String), /// func { | } /// @@ -639,6 +636,10 @@ pub enum Keyword { /// Adds as a parent type of . /// equivalent to "" "" dyn-include Include(String, String), + /// use : + /// + /// equivalent to ":" dyn-use + Use(String), /// while { } { } /// /// If wordsA result in a truthy value being on the top of the stack, execute wordsB, and @@ -656,6 +657,12 @@ pub enum Keyword { /// equivalent to def <...> =<...> def = /// or "<...>" dyn-def "=<...>" dyn-call "" dyn-def "=" dyn-call With(Vec), + /// catch [ <...>] { } with { } + /// + /// Catches errors that happen within , running when an error is + /// encountered and the error is of (or, if no type is specified, any error). + /// equivalent to \[ ["" <...>] \] { | } { | } dyn-catch + Catch(Vec, Words, Words), } /// Any SPL value that is not a construct. @@ -978,6 +985,34 @@ impl Object { } } +impl From for Object { + fn from(value: String) -> Self { + Value::Str(value).into() + } +} + +impl From for Object { + fn from(value: FrameInfo) -> Self { + let mut obj = Object::new( + get_type("FrameInfo").expect("FrameInfo type must exist"), + Value::Null, + ); + obj.property_map.insert("file".to_owned(), value.file.spl()); + obj.property_map + .insert("function".to_owned(), value.function.spl()); + obj + } +} + +impl From> for Object +where + T: Into, +{ + fn from(value: Vec) -> Self { + Value::Array(value.into_iter().map(|x| x.spl()).collect()).into() + } +} + impl From for Object { fn from(value: Value) -> Self { Object::new( @@ -1105,17 +1140,32 @@ impl Words { Keyword::Include(ta, tb) => { let rstack = &stack; runtime(move |rt| { - rt.get_type_by_name(tb.clone()) + rt.get_type_by_name(&tb) .ok_or_else(|| rstack.error(ErrorKind::TypeNotFound(tb)))? .lock() .parents .push( - rt.get_type_by_name(ta.clone()) + rt.get_type_by_name(&ta) .ok_or_else(|| rstack.error(ErrorKind::TypeNotFound(ta)))?, ); Ok(()) })?; } + Keyword::Use(item) => { + if let Some((a, mut name)) = item.split_once(':') { + let mut f = stack.get_var(a.to_owned())?; + while let Some((a, b)) = name.split_once(':') { + name = b; + let o = f.lock_ro(); + let nf = o.field(a, stack)?; + mem::drop(o); + f = nf; + } + stack.define_var(name.to_owned()); + let o = f.lock_ro().field(name, stack)?.clone(); + stack.set_var(name.to_owned(), o)?; + } + } Keyword::While(cond, blk) => loop { cond.exec(stack)?; if !stack.pop().lock_ro().is_truthy() { @@ -1123,6 +1173,7 @@ impl Words { } blk.exec(stack)?; if stack.return_accumultor > 0 { + stack.return_accumultor -= 1; break; } }, @@ -1131,6 +1182,16 @@ impl Words { blk.exec(stack)?; } } + Keyword::Catch(types, blk, ctch) => { + if let Err(e) = blk.exec(stack) { + if types.is_empty() || types.contains(&e.kind.to_string()) { + stack.push(e.spl()); + ctch.exec(stack)?; + } else { + return Err(e); + } + } + } Keyword::With(vars) => { for var in vars.into_iter().rev() { stack.define_var(var.clone()); @@ -1186,9 +1247,8 @@ impl Words { let f0 = o.kind.lock_ro(); let f = f0 .get_fn(x.clone()) - .ok_or_else(|| Error { - kind: ErrorKind::MethodNotFound(f0.name.clone(), x.clone()), - stack: stack.trace(), + .ok_or_else(|| { + stack.error(ErrorKind::MethodNotFound(f0.name.clone(), x.clone())) })? .clone(); if option_env!("SPLDEBUG").is_some() { @@ -1254,11 +1314,31 @@ pub enum ErrorKind { CustomObject(AMObject), } +impl Display for ErrorKind { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + ErrorKind::Parse(_, _) => f.write_str("Parse"), + ErrorKind::InvalidCall(_) => f.write_str("InvalidCall"), + ErrorKind::InvalidType(_, _) => f.write_str("InvalidType"), + ErrorKind::VariableNotFound(_) => f.write_str("VariableNotFound"), + ErrorKind::FuncNotFound(_) => f.write_str("FuncNotFound"), + ErrorKind::MethodNotFound(_, _) => f.write_str("MethodNotFound"), + ErrorKind::PropertyNotFound(_, _) => f.write_str("PropertyNotFound"), + ErrorKind::TypeNotFound(_) => f.write_str("TypeNotFound"), + ErrorKind::LexError(_) => f.write_str("LexError"), + ErrorKind::IO(_) => f.write_str("IO"), + ErrorKind::Custom(_) => f.write_str("Custom"), + ErrorKind::CustomObject(_) => f.write_str("CustomObject"), + } + } +} + /// Wrapper for ErrorKind with the stack trace. #[derive(PartialEq, Eq)] pub struct Error { pub kind: ErrorKind, pub stack: Vec, + pub mr_stack: Vec>, } impl Debug for Error { @@ -1271,3 +1351,28 @@ impl Debug for Error { Ok(()) } } + +impl From for Object { + fn from(value: Error) -> Self { + let mut obj = Object::new( + get_type("error").expect("error type must exist"), + Value::Null, + ); + obj.property_map + .insert("kind".to_owned(), value.kind.to_string().spl()); + obj.property_map + .insert("message".to_owned(), format!("{:?}", value.kind).spl()); + if let ErrorKind::CustomObject(ref o) = value.kind { + obj.property_map.insert("object".to_owned(), o.clone()); + } + if let ErrorKind::Custom(ref s) = value.kind { + obj.property_map + .insert("message".to_owned(), s.clone().spl()); + } + obj.property_map + .insert("trace".to_owned(), value.stack.spl()); + obj.property_map + .insert("mr-trace".to_owned(), value.mr_stack.spl()); + obj + } +} diff --git a/src/std_fns.rs b/src/std_fns.rs index e9f1ea1..91076ca 100644 --- a/src/std_fns.rs +++ b/src/std_fns.rs @@ -9,12 +9,7 @@ use std::{ sync::Arc, }; -use crate::{ - dyn_fns, - mutex::Mut, - runtime::*, - *, -}; +use crate::{dyn_fns, mutex::Mut, runtime::*, *}; #[macro_export] macro_rules! type_err { @@ -77,7 +72,7 @@ pub fn settype(stack: &mut Stack) -> OError { return stack.err(ErrorKind::InvalidCall("settype".to_owned())) }; let o = stack.pop(); - let kind = runtime(|rt| rt.get_type_by_name(s.clone())) + let kind = runtime(|rt| rt.get_type_by_name(&s)) .ok_or_else(|| stack.error(ErrorKind::TypeNotFound(s)))?; let mut obj = o.lock(); kind.lock_ro().write_into(&mut obj); @@ -485,31 +480,11 @@ pub fn trace(stack: &mut Stack) -> OError { pub fn mr_trace(stack: &mut Stack) -> OError { let trace = stack.mr_trace(); - let kind = runtime(|rt| rt.get_type_by_name("TraceElement".to_owned())) - .ok_or_else(|| stack.error(ErrorKind::TypeNotFound("TraceElement".to_owned())))?; stack.push( Value::Array( trace .into_iter() - .map(|x| { - Value::Array( - x.into_iter() - .map(|x| { - let item = Value::Null.spl(); - let mut obj = item.lock(); - kind.lock_ro().write_into(&mut obj); - obj.kind = kind.clone(); - obj.property_map - .insert("file".to_owned(), Value::Str(x.file).spl()); - obj.property_map - .insert("function".to_owned(), Value::Str(x.function).spl()); - mem::drop(obj); - item - }) - .collect(), - ) - .spl() - }) + .map(|x| Value::Array(x.into_iter().map(|x| x.spl()).collect()).spl()) .collect(), ) .spl(), @@ -784,9 +759,14 @@ pub fn acopy(stack: &mut Stack) -> OError { Ok(()) } +pub fn throw(stack: &mut Stack) -> OError { + let kind = ErrorKind::CustomObject(stack.pop()); + stack.err(kind) +} + pub fn register(r: &mut Stack, o: Arc) { type Fn = fn(&mut Stack) -> OError; - let fns: [(&str, Fn, u32); 49] = [ + let fns: [(&str, Fn, u32); 50] = [ ("pop", pop, 0), ("dup", dup, 2), ("clone", clone, 1), @@ -836,6 +816,7 @@ pub fn register(r: &mut Stack, o: Arc) { ("str-to-bytes", str_to_bytes, 1), ("bytes-to-str", bytes_to_str, 1), ("acopy", acopy, 1), + ("throw", throw, 0), ]; for f in fns { r.define_func( diff --git a/std.spl b/std.spl index bbe7d0b..7527e22 100644 --- a/std.spl +++ b/std.spl @@ -8,22 +8,57 @@ func println { | print "\n" print } +construct error { + kind + message + object + trace + mr-trace +} + +construct FrameInfo { + file + function +} + construct _str_ext { ; new { any | with this ; null clone this settype:construct } to-bytes { [int] | str-to-bytes } + split { str | with splitter this ; + def bytes splitter:to-bytes =bytes + def iter this:to-bytes:iter =iter + def item 0 =item + [ while { item null eq not } { + def match 0 =match + [ + while { match bytes:len eq not } { + iter:next =item + item null eq if { + 3 stop + } + item dup (match bytes:get) eq dup if { + match ++ =match + } not if { + 0 =match + } + } + { | pop pop } match:foreach + ] _str + } ] + } } include _str_ext in str construct _mega-ext { ; swap { .. | with this ; - i mswap - i -- mswap + this mswap + this -- mswap } mswap { .. | mswap } - foreach { | with this ; + foreach { | with callable this ; def i 0 =i while { i this lt } { i callable call i ++ =i } } diff --git a/test.spl b/test.spl index 936332f..332db24 100644 --- a/test.spl +++ b/test.spl @@ -107,11 +107,15 @@ func main { int | with args ; "hi\n" _:to-bytes file:write-exact; file:close null =file + "" println + "testing split" println + { | println } (" " "hello how are you" _:split):foreach "" println + use net:http:Request "testing http" println - def req "tudbut.de" 80 "GET" "/" net:http:Request:new =req - req:send + def req "tudbut.de" 81 "GET" "/" Request:new =req + req:send:body _str println "" println 100