//
// Copyright 2025 Formata, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
use std::sync::Arc;
use arcstr::literal;
use imbl::vector;
use nom::{branch::alt, bytes::complete::tag, character::complete::{char, multispace0, one_of}, combinator::{opt, peek, recognize}, multi::{many1, separated_list0, separated_list1}, sequence::{delimited, preceded, separated_pair, terminated}, IResult, Parser};
use crate::{model::SId, parser::{doc::StofParseError, expr::{fmt_str::formatted_string_expr, func::func_expr, graph::{call_expr, chained_var_func, graph_expr}, literal::literal_expr, math::math_expr, new_obj::new_obj_expr}, statement::{block, switch::switch_statement}, types::parse_type, whitespace::whitespace}, runtime::{instruction::{Instruction, Instructions}, instructions::{block::Block, call::FuncCall, ifs::IfIns, list::{ListIns, NEW_LIST}, map::{MapIns, NEW_MAP}, nullcheck::NullcheckIns, set::{SetIns, NEW_SET}, tup::{TupIns, NEW_TUP}, Base, AWAIT, NOOP, NOT_TRUTHY, POP_RETURN, PUSH_RETURN, SUSPEND, TYPE_NAME, TYPE_OF}, Type, Val}};
pub mod literal;
pub mod math;
pub mod graph;
pub mod func;
pub mod fmt_str;
pub mod new_obj;
/// Parse an expression.
pub fn expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
let (input, mut ins) = alt([
new_obj_expr,
func_expr,
await_expr,
async_expr,
typename_expr,
typeof_expr,
tup_expr,
list_expr,
blob_expr,
set_expr,
map_expr,
math_expr,
not_expr,
block_expr,
switch_expr,
literal_expr,
formatted_string_expr,
graph_expr,
wrapped_expr,
]).parse(input)?;
// Peek at the next value, if its async, then don't do the as below...
let (mut input, peek_async) = opt(peek(preceded(multispace0, tag("async")))).parse(input)?;
if peek_async.is_none() {
// Optional "as Type" cast before other operators
let (inner_input, cast_type) = opt(preceded(delimited(multispace0, tag("as"), multispace0), parse_type)).parse(input)?;
if let Some(cast) = cast_type {
let mut block = Block::default();
block.ins.push_back(ins);
block.ins.push_back(Arc::new(Base::Cast(cast)));
input = inner_input;
ins = Arc::new(block);
}
}
// Nullcheck operator "??"
let (input, nullcheck) = opt(preceded(delimited(multispace0, tag("??"), multispace0), expr)).parse(input)?;
if let Some(nullcheck) = nullcheck {
ins = Arc::new(NullcheckIns {
ins,
ifnull: nullcheck
});
}
// Ternary operator "?"
let (input, ternary) = opt(ternary_operator).parse(input)?;
if let Some((if_expr, else_expr)) = ternary {
ins = Arc::new(IfIns {
if_test: Some(ins),
if_ins: vector![if_expr],
el_ins: vector![else_expr],
});
}
Ok((input, ins))
}
/// Ternary operator.
/// test_expr ? if_expr : else_expr
fn ternary_operator(input: &str) -> IResult<&str, (Arc<dyn Instruction>, Arc<dyn Instruction>), StofParseError> {
let (input, _) = delimited(multispace0, char('?'), multispace0).parse(input)?;
let (input, if_expr) = terminated(expr, delimited(multispace0, char(':'), multispace0)).parse(input)?;
let (input, else_expr) = expr(input)?;
Ok((input, (if_expr, else_expr)))
}
/// Block expression.
pub fn block_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
let (input, mut statements) = block(input)?;
if statements.is_empty() { return Ok((input, NOOP.clone())); }
// return statements within a block expression return at the block, not the function, so spoof it
let tmp = SId::default();
statements.push_front(PUSH_RETURN.clone());
statements.push_front(Arc::new(Base::Literal(Val::Fn(tmp.clone()))));
statements.push_back(Arc::new(Base::Tag(tmp.as_ref().into())));
statements.push_back(POP_RETURN.clone());
Ok((input, Arc::new(Block { ins: statements })))
}
/// Switch expression.
pub fn switch_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
let (input, mut statements) = switch_statement(input)?;
if statements.is_empty() { return Ok((input, NOOP.clone())); }
// return statements within a block expression return at the block, not the function, so spoof it
let tmp = SId::default();
statements.push_front(PUSH_RETURN.clone());
statements.push_front(Arc::new(Base::Literal(Val::Fn(tmp.clone()))));
statements.push_back(Arc::new(Base::Tag(tmp.as_ref().into())));
statements.push_back(POP_RETURN.clone());
Ok((input, Arc::new(Block { ins: statements })))
}
/// Blob expression.
pub fn blob_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
let (input, _) = whitespace(input)?;
let (input, bytes) = delimited(
char('|'),
terminated(separated_list1(char(','), blob_number), whitespace),
alt((
preceded(char(','), preceded(whitespace, char('|'))),
char('|')
))
).parse(input)?;
Ok((input, Arc::new(Base::Literal(Val::Blob(bytes.into())))))
}
pub fn blob_number(input: &str) -> IResult<&str, u8, StofParseError> {
let (input, _) = whitespace(input)?;
let (input, recognized) = recognize(many1(one_of("0123456789"))).parse(input)?;
let value = recognized.parse::<u8>().expect("could not parse floating point number");
Ok((input, value))
}
/// List contructor expression.
pub fn list_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
let (input, _) = whitespace(input)?;
let (input, exprs) = delimited(
char('['),
terminated(separated_list0(char(','), expr), whitespace),
alt((
preceded(char(','), preceded(whitespace, char(']'))),
char(']')
))
).parse(input)?;
// Optional chained calls here
// Ex. [3, 4].at(0)
let (input, additional) = opt(preceded(char('.'), separated_list1(char('.'), chained_var_func))).parse(input)?;
let mut block = Block::default();
block.ins.push_back(NEW_LIST.clone());
for expr in exprs {
block.ins.push_back(Arc::new(ListIns::AppendList(expr)));
}
if let Some(additional) = additional {
for ins in additional {
block.ins.push_back(ins);
}
}
Ok((input, Arc::new(block)))
}
/// Tuple contructor expression.
pub fn tup_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
let (input, _) = whitespace(input)?;
let (input, exprs) = delimited(
char('('),
terminated(separated_list1(char(','), expr), whitespace),
alt((
preceded(char(','), preceded(whitespace, char(')'))),
char(')')
))
).parse(input)?;
if exprs.len() < 2 {
return Err(nom::Err::Error(StofParseError::from(format!("tuple constructor requires at least 2 values"))));
}
// Optional chained calls here
// Ex. (3, 4).at(0)
let (input, additional) = opt(preceded(char('.'), separated_list1(char('.'), chained_var_func))).parse(input)?;
let mut block = Block::default();
block.ins.push_back(NEW_TUP.clone());
for expr in exprs {
block.ins.push_back(Arc::new(TupIns::AppendTup(expr)));
}
if let Some(additional) = additional {
for ins in additional {
block.ins.push_back(ins);
}
}
Ok((input, Arc::new(block)))
}
/// Set contructor expression.
pub fn set_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
let (input, _) = whitespace(input)?;
let (input, exprs) = delimited(
char('{'),
terminated(separated_list0(char(','), expr), whitespace),
alt((
preceded(char(','), preceded(whitespace, char('}'))),
char('}')
))
).parse(input)?;
// Optional chained calls here
// Ex. {3, 4}.at(0)
let (input, additional) = opt(preceded(char('.'), separated_list1(char('.'), chained_var_func))).parse(input)?;
let mut block = Block::default();
block.ins.push_back(NEW_SET.clone());
for expr in exprs {
block.ins.push_back(Arc::new(SetIns::AppendSet(expr)));
}
if let Some(additional) = additional {
for ins in additional {
block.ins.push_back(ins);
}
}
Ok((input, Arc::new(block)))
}
/// Map contructor expression.
pub fn map_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
let (input, _) = whitespace(input)?;
let (input, exprs) = delimited(
char('{'),
terminated(separated_list0(char(','), separated_pair(expr, char(':'), expr)), whitespace),
alt((
preceded(char(','), preceded(whitespace, char('}'))),
char('}')
))
).parse(input)?;
// Optional chained calls here
// Ex. {'a': 3, 'b': 4}.at('b')
let (input, additional) = opt(preceded(char('.'), separated_list1(char('.'), chained_var_func))).parse(input)?;
let mut block = Block::default();
block.ins.push_back(NEW_MAP.clone());
for expr in exprs {
block.ins.push_back(Arc::new(MapIns::AppendMap(expr)));
}
if let Some(additional) = additional {
for ins in additional {
block.ins.push_back(ins);
}
}
Ok((input, Arc::new(block)))
}
/// Await expression.
pub fn await_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
let (input, _) = whitespace(input)?;
let (input, ins) = preceded(tag("await"), expr).parse(input)?;
let mut block = Block::default();
block.ins.push_back(ins); // a promise (maybe)
block.ins.push_back(AWAIT.clone()); // will only do something if its a promise
Ok((input, Arc::new(block)))
}
/// Async expression.
/// Spawns a new process with an expr.
pub fn async_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
let (input, _) = whitespace(input)?;
let (input, ins) = preceded(tag("async"), expr).parse(input)?;
// Spawn will put a new Promise<unknown> on the stack for you use if you want
let mut block = Block::default();
block.ins.push_back(Arc::new(Base::Spawn((Instructions::from(ins), Type::Void)))); // no casting by default
block.ins.push_back(SUSPEND.clone()); // start the new process right away
Ok((input, Arc::new(block)))
}
/// Wrapped expression.
pub fn wrapped_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
let (input, _) = whitespace(input)?;
let (input, mut ins) = delimited(char('('), delimited(multispace0, expr, multispace0), char(')')).parse(input)?;
let (mut input, additional) = opt(preceded(char('.'), separated_list1(char('.'), chained_var_func))).parse(input)?;
if additional.is_some() {
let mut block = Block::default();
block.ins.push_back(ins);
if let Some(additional) = additional {
for ins in additional {
block.ins.push_back(ins);
}
}
ins = Arc::new(block);
} else {
let (rest, call_args) = opt(call_expr).parse(input)?;
if call_args.is_some() {
input = rest;
let mut block = Block::default();
block.ins.push_back(ins);
block.ins.push_back(Arc::new(FuncCall {
as_ref: false,
cnull: false,
func: None,
search: Some(literal!("")),
stack: true,
args: call_args.unwrap().into_iter().collect(),
oself: None,
}));
ins = Arc::new(block);
}
}
Ok((input, ins))
}
/// Not expression.
pub fn not_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
let (input, _) = whitespace(input)?;
let (input, ins) = preceded(char('!'), expr).parse(input)?;
let mut block = Block::default();
block.ins.push_back(ins);
block.ins.push_back(NOT_TRUTHY.clone());
Ok((input, Arc::new(block)))
}
/// TypeOf expression.
pub fn typeof_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
let (input, _) = whitespace(input)?;
let (input, ins) = preceded(tag("typeof"), expr).parse(input)?;
let mut block = Block::default();
block.ins.push_back(ins);
block.ins.push_back(TYPE_OF.clone());
Ok((input, Arc::new(block)))
}
/// TypeName expression.
/// A specific type instead of a general one Ex. "MyObj" instead of "obj"
pub fn typename_expr(input: &str) -> IResult<&str, Arc<dyn Instruction>, StofParseError> {
let (input, _) = whitespace(input)?;
let (input, ins) = preceded(tag("typename"), expr).parse(input)?;
let mut block = Block::default();
block.ins.push_back(ins);
block.ins.push_back(TYPE_NAME.clone());
Ok((input, Arc::new(block)))
}
#[cfg(test)]
mod tests {
use crate::{model::Graph, parser::expr::expr, runtime::Runtime};
#[test]
fn parse_map_expr() {
let (_input, res) = expr("{'a': 1, 'b': 2, 'c': 3}").unwrap();
let mut graph = Graph::default();
let res = Runtime::eval(&mut graph, res).unwrap();
//println!("{}", res.print(&graph));
assert!(res.map());
}
#[test]
fn parse_list_expr() {
let (_input, res) = expr("['a', 2, 'c']").unwrap();
let mut graph = Graph::default();
let res = Runtime::eval(&mut graph, res).unwrap();
//println!("{}", res.print(&graph));
assert!(res.list());
}
#[test]
fn parse_tup_expr() {
let (_input, res) = expr("('a', 2, 'c')").unwrap();
let mut graph = Graph::default();
let res = Runtime::eval(&mut graph, res).unwrap();
//println!("{}", res.print(&graph));
assert!(res.tup());
}
#[test]
fn parse_set_expr() {
let (_input, res) = expr("{'a', 2, 'c'}").unwrap();
let mut graph = Graph::default();
let res = Runtime::eval(&mut graph, res).unwrap();
//println!("{}", res.print(&graph));
assert!(res.set());
}
#[test]
fn parse_wrapped_expr() {
let (_input, res) = expr("(['a', 2, 'c'])").unwrap();
let mut graph = Graph::default();
let res = Runtime::eval(&mut graph, res).unwrap();
//println!("{}", res.print(&graph));
assert!(res.list());
}
#[test]
fn await_passthrough_expr() {
let (_input, res) = expr("await 42").unwrap();
let mut graph = Graph::default();
let res = Runtime::eval(&mut graph, res).unwrap();
assert_eq!(res, 42.into());
}
}