add isbpl compatibility mode for basic programs
This commit is contained in:
parent
fa60d4c1e0
commit
574ea328ed
12 changed files with 159 additions and 45 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -22,7 +22,7 @@ checksum = "b03f7fbd470aa8b3ad163c85cce8bccfc11cc9c44ef12da0a4eddd98bd307352"
|
|||
|
||||
[[package]]
|
||||
name = "spl"
|
||||
version = "0.2.3"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"multicall",
|
||||
"once_cell",
|
||||
|
|
|
@ -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"
|
||||
|
|
11
assemble.spl
11
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
|
||||
}
|
||||
|
|
23
isbpl.spl
Normal file
23
isbpl.spl
Normal 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
|
||||
}
|
|
@ -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(),
|
||||
|
|
85
src/lexer.rs
85
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<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
31
src/lexer/compat.rs
Normal 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,
|
||||
}
|
||||
}
|
20
src/lib.rs
20
src/lib.rs
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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...");
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
4
std.spl
4
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 { |
|
||||
|
|
Loading…
Add table
Reference in a new issue