add isbpl compatibility mode for basic programs

This commit is contained in:
Daniella / Tove 2024-09-06 18:54:54 +02:00
parent fa60d4c1e0
commit 574ea328ed
12 changed files with 159 additions and 45 deletions

2
Cargo.lock generated
View file

@ -22,7 +22,7 @@ checksum = "b03f7fbd470aa8b3ad163c85cce8bccfc11cc9c44ef12da0a4eddd98bd307352"
[[package]]
name = "spl"
version = "0.2.3"
version = "0.3.0"
dependencies = [
"multicall",
"once_cell",

View file

@ -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"

View file

@ -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
}

23
isbpl.spl Normal file
View file

@ -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
}

View file

@ -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(),

View file

@ -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<Words, LexerError> {
pub fn lex(compat: bool, input: String) -> Result<Words, LexerError> {
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<u32>, Words, usize), LexerError> {
fn read_block(
str_words: &[String],
isfn: bool,
mut compat: bool,
) -> Result<(Option<u32>, 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<u32>, 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<u32>, 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<u32>, 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<u32>, 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<u32>, 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<u32>, 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)))
}

31
src/lexer/compat.rs Normal file
View file

@ -0,0 +1,31 @@
use crate::Word;
pub(crate) fn match_compat(
word: String,
str_words: &[String],
words: &mut Vec<Word>,
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,
}
}

View file

@ -55,6 +55,14 @@ pub fn start_file_in_runtime(path: &str) -> Result<Stack, Error> {
// 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<Stack, Error> {
/// 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)
}

View file

@ -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...");
}

View file

@ -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))

View file

@ -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);
}
}

View file

@ -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 { |