From 574ea328edccb455bd794d6ca7539eb8d492d404 Mon Sep 17 00:00:00 2001 From: TudbuT Date: Fri, 6 Sep 2024 18:54:54 +0200 Subject: [PATCH] add isbpl compatibility mode for basic programs --- Cargo.lock | 2 +- Cargo.toml | 4 +-- assemble.spl | 11 +++++- isbpl.spl | 23 ++++++++++++ src/dyn_fns.rs | 6 ++-- src/lexer.rs | 85 +++++++++++++++++++++++++++++++-------------- src/lexer/compat.rs | 31 +++++++++++++++++ src/lib.rs | 20 +++++++---- src/main.rs | 9 ++--- src/std_fns.rs | 3 +- src/stdlib.rs | 6 ++++ std.spl | 4 +-- 12 files changed, 159 insertions(+), 45 deletions(-) create mode 100644 isbpl.spl create mode 100644 src/lexer/compat.rs diff --git a/Cargo.lock b/Cargo.lock index 81d32a4..9da27c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,7 +22,7 @@ checksum = "b03f7fbd470aa8b3ad163c85cce8bccfc11cc9c44ef12da0a4eddd98bd307352" [[package]] name = "spl" -version = "0.2.3" +version = "0.3.0" dependencies = [ "multicall", "once_cell", diff --git a/Cargo.toml b/Cargo.toml index 921762d..cb3fc34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl" -version = "0.2.4" +version = "0.3.0" edition = "2021" description = "Stack Pogramming Language: A simple, concise scripting language." license = "MIT" @@ -10,4 +10,4 @@ authors = ["TudbuT"] [dependencies] readformat = "0.1" once_cell = "1.17" -multicall = "0.1.4" +multicall = "0.1" diff --git a/assemble.spl b/assemble.spl index 9c17c9e..58758af 100644 --- a/assemble.spl +++ b/assemble.spl @@ -1,5 +1,14 @@ func main { mega | with args ; - 2 args:get write-file-sasm println + args:len 3 gt if { + 3 args:get "isbpl" eq if { + "const str import-isbpl" println + "#isbpl.spl" println + "end import-isbpl" println + "call import" println + } + } + def file 2 args:get =file + file write-file-sasm println 0 } diff --git a/isbpl.spl b/isbpl.spl new file mode 100644 index 0000000..c640f21 --- /dev/null +++ b/isbpl.spl @@ -0,0 +1,23 @@ +native engage-compatibility-mode +func # { pop } +func puts { print } +func fcall { call } +func strconcat { concat } +func aget { swap :get } +func stoi { _mega } +func itos { _str } +def argv'pre-isbplmod &argv =argv'pre-isbplmod +func argv { + def args argv'pre-isbplmod call =args + def nargs args:len 2 - anew =nargs + args nargs 2 0 nargs:len acopy + nargs +} +func inc { + "isbpl.spl: ISBPL code tried to call inc, an untranslatable function" println + pop +} +func dec { + "isbpl.spl: ISBPL code tried to call inc, an untranslatable function" println + pop +} diff --git a/src/dyn_fns.rs b/src/dyn_fns.rs index 43502ae..44cf647 100644 --- a/src/dyn_fns.rs +++ b/src/dyn_fns.rs @@ -195,7 +195,8 @@ pub fn dyn_read(stack: &mut Stack) -> OError { Value::Func(AFunc::new(Func { ret_count: 0, to_call: FuncImpl::SPL( - lexer::lex(s).map_err(|x| stack.error(ErrorKind::LexError(format!("{x:?}"))))?, + lexer::lex(false, s) + .map_err(|x| stack.error(ErrorKind::LexError(format!("{x:?}"))))?, ), run_as_base: false, origin: stack.get_frame(), @@ -218,7 +219,8 @@ pub fn dyn_readf(stack: &mut Stack) -> OError { Value::Func(AFunc::new(Func { ret_count: 0, to_call: FuncImpl::SPL( - lexer::lex(s).map_err(|x| stack.error(ErrorKind::LexError(format!("{x:?}"))))?, + lexer::lex(n.ends_with(".isbpl"), s) + .map_err(|x| stack.error(ErrorKind::LexError(format!("{x:?}"))))?, ), run_as_base: true, origin: stack.get_frame(), diff --git a/src/lexer.rs b/src/lexer.rs index d4cb88a..abe8524 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -1,6 +1,9 @@ +mod compat; + use std::{mem, sync::Arc}; use crate::runtime::*; +use compat::match_compat; use readformat::*; #[derive(Debug, PartialEq, Eq)] @@ -13,12 +16,16 @@ pub enum LexerError { ArgsWithoutCall, } -pub fn lex(input: String) -> Result { +pub fn lex(compat: bool, input: String) -> Result { let str_words = parse(input); - Ok(read_block(&str_words[..], false)?.1) + Ok(read_block(&str_words[..], false, compat)?.1) } -fn read_block(str_words: &[String], isfn: bool) -> Result<(Option, Words, usize), LexerError> { +fn read_block( + str_words: &[String], + isfn: bool, + mut compat: bool, +) -> Result<(Option, Words, usize), LexerError> { if str_words.is_empty() { return Ok((None, Words::new(Vec::new()), 0)); } @@ -26,45 +33,71 @@ fn read_block(str_words: &[String], isfn: bool) -> Result<(Option, Words, u let mut words = Vec::new(); let mut i = 0; if str_words[0] == "{" && isfn { - let mut r = 0_u32; - while str_words[r as usize + 1] != "|" { + let mut r = 0; + while str_words[r + 1] != "|" && !compat { r += 1; + if r >= str_words.len() - 1 { + r = 0; + if !compat { + eprintln!("spl: parsing in compatibility mode"); + compat = true; + } + } } - i += r as usize + 2; - rem = Some(r); + i += r + 2 - compat as usize; + rem = Some(r as u32); } while i < str_words.len() { let word = str_words[i].to_owned(); + if compat && match_compat(word.to_owned(), str_words, &mut words, &mut i) { + continue; + } match word.as_str() { + "native" => { + i += 1; + if !compat { + eprintln!("spl: parsing in compatibility mode"); + compat = true; + } + eprintln!( + "spl: [compat] code tried to load a native function `{}`", + str_words[i] + ); + } "def" => { words.push(Word::Key(Keyword::Def(str_words[i + 1].to_owned()))); i += 1; } "func" => { + if compat && str_words[i + 1] == "{" { + continue; + } if let Some(dat) = readf1("func\0{}\0{", str_words[i..=i + 2].join("\0").as_str()) { - let block = read_block(&str_words[i + 2..], true)?; + let block = read_block(&str_words[i + 2..], true, compat)?; i += 2 + block.2; words.push(Word::Key(Keyword::Func( dat.to_owned(), block.0.ok_or(LexerError::FunctionBlockExpected)?, block.1, ))); - } else if let Some(dat) = - readf1("func\0{}\0@rust", str_words[i..=i + 2].join("\0").as_str()) - { - i += 3; - words.push(Word::Key(Keyword::FuncOf( - dat.to_owned(), - str_words[i][2..].to_owned(), - FuncImplType::Rust, - ))); + } else if !compat { + if let Some(dat) = + readf1("func\0{}\0@rust", str_words[i..=i + 2].join("\0").as_str()) + { + i += 3; + words.push(Word::Key(Keyword::FuncOf( + dat.to_owned(), + str_words[i][2..].to_owned(), + FuncImplType::Rust, + ))); + } } else { return Err(LexerError::FunctionBlockExpected); } } // lambda "{" => { - let block = read_block(&str_words[i..], true)?; + let block = read_block(&str_words[i..], true, compat)?; i += block.2; words.push(Word::Const(Value::Func(AFunc::new(Func { ret_count: block.0.ok_or(LexerError::FunctionBlockExpected)?, @@ -79,7 +112,7 @@ fn read_block(str_words: &[String], isfn: bool) -> Result<(Option, Words, u words.push(Word::Const(Value::Str(x[2..].to_owned()))); } "<{" => { - let block = read_block(&str_words[i + 1..], false)?; + let block = read_block(&str_words[i + 1..], false, compat)?; i += block.2 + 1; let mut block = block.1.words; match words.remove(words.len() - 1) { @@ -122,7 +155,7 @@ fn read_block(str_words: &[String], isfn: bool) -> Result<(Option, Words, u if name == "construct" { has_construct = true; } - let block = read_block(&str_words[i + 1..], true)?; + let block = read_block(&str_words[i + 1..], true, compat)?; i += 1 + block.2; methods.push(( name, @@ -141,7 +174,7 @@ fn read_block(str_words: &[String], isfn: bool) -> Result<(Option, Words, u is_namespace, ))); } - "include" => { + "include" if !compat => { if let Some(x) = readf( "include\0{}\0in\0{}", str_words[i..i + 4].join("\0").as_str(), @@ -161,14 +194,14 @@ fn read_block(str_words: &[String], isfn: bool) -> Result<(Option, Words, u words.push(Word::Key(Keyword::Use(item))); } "while" => { - let cond = read_block(&str_words[i + 2..], false)?; + let cond = read_block(&str_words[i + 2..], false, compat)?; i += 2 + cond.2; - let blk = read_block(&str_words[i + 2..], false)?; + let blk = read_block(&str_words[i + 2..], false, compat)?; i += 2 + blk.2; words.push(Word::Key(Keyword::While(cond.1, blk.1))); } "if" => { - let blk = read_block(&str_words[i + 2..], false)?; + let blk = read_block(&str_words[i + 2..], false, compat)?; i += 2 + blk.2; words.push(Word::Key(Keyword::If(blk.1))); } @@ -179,9 +212,9 @@ fn read_block(str_words: &[String], isfn: bool) -> Result<(Option, Words, u types.push(str_words[i].to_owned()); i += 1; } - let blk = read_block(&str_words[i + 1..], false)?; + let blk = read_block(&str_words[i + 1..], false, compat)?; i += 2 + blk.2; - let ctch = read_block(&str_words[i + 1..], false)?; + let ctch = read_block(&str_words[i + 1..], false, compat)?; i += 1 + ctch.2; words.push(Word::Key(Keyword::Catch(types, blk.1, ctch.1))) } diff --git a/src/lexer/compat.rs b/src/lexer/compat.rs new file mode 100644 index 0000000..ee210a4 --- /dev/null +++ b/src/lexer/compat.rs @@ -0,0 +1,31 @@ +use crate::Word; + +pub(crate) fn match_compat( + word: String, + str_words: &[String], + words: &mut Vec, + i: &mut usize, +) -> bool { + match word.as_str() { + "inc" => { + eprintln!("spl: [compat] transforming inc call"); + words.push(Word::Call("++".to_owned(), false, 0)); + words.push(Word::Call("=".to_owned() + &str_words[*i - 1], false, 0)); + *i += 1; + true + } + "dec" => { + eprintln!("spl: [compat] transforming dec call"); + words.push(Word::Call("--".to_owned(), false, 0)); + words.push(Word::Call("=".to_owned() + &str_words[*i - 1], false, 0)); + *i += 1; + true + } + "include" => { + // TODO: translate some stdlib components? + words.push(Word::Call("import".to_owned(), false, 0)); + true + } + _ => false, + } +} diff --git a/src/lib.rs b/src/lib.rs index 809a447..cd870b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,6 +55,14 @@ pub fn start_file_in_runtime(path: &str) -> Result { // import stdlib add_std(&mut stack)?; + if path.ends_with(".isbpl") { + Words::new(vec![ + Word::Const(Value::Str("#isbpl.spl".to_owned())), + Word::Call("import".to_owned(), false, 0), + ]) + .exec(&mut stack)?; + } + // run file Words::new(vec![ Word::Const(Value::Str(path.to_owned())), @@ -68,12 +76,12 @@ pub fn start_file_in_runtime(path: &str) -> Result { /// 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 { - fs::read_to_string(f).unwrap() - } else { - f.unwrap_err() - }) - .map_err(|x| stack.error(ErrorKind::LexError(format!("{x:?}"))))?; + let words = f + .map_err(|x| stack.error(ErrorKind::LexError(format!("{x}")))) + .and_then(|f| { + lex(f.ends_with(".isbpl"), fs::read_to_string(f).unwrap()) + .map_err(|x| stack.error(ErrorKind::LexError(format!("{x:?}")))) + })?; words.exec(stack) } diff --git a/src/main.rs b/src/main.rs index 63379c5..ae0e056 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,6 @@ -use spl::{find_in_splpath, lex, oxidizer::RustAppBuilder, start_file_in_runtime, Runtime, SetRuntime}; +use spl::{ + find_in_splpath, lex, oxidizer::RustAppBuilder, start_file_in_runtime, Runtime, SetRuntime, +}; use std::{env::args, fs}; @@ -21,15 +23,14 @@ fn main() { builder.set_name(name); } println!("Embedding source..."); - } - else { + } else { builder.set_name(file[..file.rfind('.').unwrap_or(file.len())].to_owned()); } builder.add_source(file.to_owned(), data.to_owned()); if build_only { println!("Preparing rust code..."); } - builder.prepare(lex(data.to_owned()).expect("invalid SPL in natives file.")); + builder.prepare(lex(false, data.to_owned()).expect("invalid SPL in natives file.")); if build_only { println!("Building..."); } diff --git a/src/std_fns.rs b/src/std_fns.rs index 6cc66d6..0d3c44d 100644 --- a/src/std_fns.rs +++ b/src/std_fns.rs @@ -815,7 +815,7 @@ pub fn write_sasm(stack: &mut Stack) -> OError { require_on_stack!(code, Str, stack, "write-sasm"); stack.push( Value::Str( - lexer::lex(code) + lexer::lex(false, code) .map(|x| sasm_write(x)) .map_err(|x| stack.error(ErrorKind::LexError(format!("{x:?}"))))?, ) @@ -829,6 +829,7 @@ pub fn write_file_sasm(stack: &mut Stack) -> OError { stack.push( Value::Str( lexer::lex( + file.ends_with(".isbpl"), fs::read_to_string(file).map_err(|x| stack.error(ErrorKind::IO(x.to_string())))?, ) .map(|x| sasm_write(x)) diff --git a/src/stdlib.rs b/src/stdlib.rs index 38c2409..fc07905 100644 --- a/src/stdlib.rs +++ b/src/stdlib.rs @@ -7,6 +7,9 @@ pub const ITER: &str = include_str!("../iter.spl"); pub const HTTP: &str = include_str!("../http.spl"); pub const STREAM: &str = include_str!("../stream.spl"); pub const MESSAGING: &str = include_str!("../messaging.spl"); +pub const ASSEMBLE: &str = include_str!("../assemble.spl"); +pub const ISBPL: &str = include_str!("../isbpl.spl"); +pub const REPL: &str = include_str!("../repl.spl"); pub fn register(runtime: &mut Runtime) { multicall! { @@ -17,5 +20,8 @@ pub fn register(runtime: &mut Runtime) { insert("http.spl", HTTP); insert("stream.spl", STREAM); insert("messaging.spl", MESSAGING); + insert("assemble.spl", ASSEMBLE); + insert("isbpl.spl", ISBPL); + insert("repl.spl", REPL); } } diff --git a/std.spl b/std.spl index 6b7cba4..0dff490 100644 --- a/std.spl +++ b/std.spl @@ -6,9 +6,9 @@ func =null { | pop def program-name -def std.alias.print &print =std.alias.print +def print'pre-std &print =print'pre-std func print { | - _str std.alias.print call + _str print'pre-std call } func println { |