use crate::{model::{InnerDoc, SId}, parser::{context::ParseContext, data::parse_data, field::parse_field, func::parse_function, ident::ident, import::import, string::{double_string, single_string}, whitespace::{parse_inner_doc_comment, whitespace, whitespace_fail}}, runtime::Error};
use nanoid::nanoid;
use nom::{branch::alt, bytes::complete::{tag, take_until}, character::complete::{char, multispace0, space0}, combinator::{eof, opt, map}, error::{ErrorKind, FromExternalError, ParseError}, sequence::{delimited, preceded}, Err, IResult, Parser};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct StofParseError {
pub file_path: Option<String>,
pub message: String,
}
impl From<&str> for StofParseError {
fn from(value: &str) -> Self {
Self {
file_path: None,
message: value.to_string(),
}
}
}
impl From<String> for StofParseError {
fn from(value: String) -> Self {
Self {
file_path: None,
message: value,
}
}
}
pub fn err_fail(e: nom::Err<StofParseError>) -> nom::Err<StofParseError> {
match e {
nom::Err::Error(e) => nom::Err::Failure(e),
_ => e
}
}
impl ParseError<&str> for StofParseError {
fn from_error_kind(input: &str, kind: ErrorKind) -> Self {
let message = format!("{:?}: {input}\n", kind, );
StofParseError { message, file_path: None }
}
fn append(_input: &str, _kind: ErrorKind, other: Self) -> Self {
other
}
fn from_char(input: &str, c: char) -> Self {
let message = format!("expected char '{c}':\n{input}");
StofParseError { message, file_path: None }
}
fn or(self, other: Self) -> Self {
other
}
}
impl FromExternalError<&str, std::num::ParseIntError> for StofParseError {
fn from_external_error(_input: &str, _kind: ErrorKind, e: std::num::ParseIntError) -> Self {
Self::from(e.to_string())
}
}
pub fn document(mut input: &str, context: &mut ParseContext) -> Result<(), Error> {
loop {
let res = document_statement(input, context);
match res {
Ok((rest, _)) => {
if rest.is_empty() { break; }
input = rest;
},
Err(error) => {
match error {
nom::Err::Error(e) => {
return Err(Error::ParseError(e));
},
nom::Err::Failure(e) => {
return Err(Error::ParseError(e));
},
nom::Err::Incomplete(_) => {
return Err(Error::ParseError(StofParseError::from(error.to_string())));
}
}
}
}
}
Ok(())
}
pub fn document_statement<'a>(input: &'a str, context: &mut ParseContext) -> IResult<&'a str, (), StofParseError> {
{
let field_res = parse_field(input, context);
match field_res {
Ok((input, _)) => {
return Ok((input, ()));
},
Err(error) => {
match error {
Err::Incomplete(_) |
Err::Error(_) => {},
Err::Failure(_) => {
return Err(error);
}
}
}
}
}
{
let func_res = parse_function(input, context);
match func_res {
Ok((input, _)) => {
return Ok((input, ()));
},
Err(error) => {
match error {
Err::Incomplete(_) |
Err::Error(_) => {},
Err::Failure(_) => {
return Err(error);
}
}
}
}
}
{
let import_res = import(input, context);
match import_res {
Ok((input, _)) => {
return Ok((input, ()));
},
Err(error) => {
match error {
Err::Incomplete(_) |
Err::Error(_) => {},
Err::Failure(_) => {
return Err(error);
}
}
}
}
}
{
let data_res = parse_data(input, context);
match data_res {
Ok((input, _)) => {
return Ok((input, ()));
},
Err(error) => {
match error {
Err::Incomplete(_) |
Err::Error(_) => {},
Err::Failure(_) => {
return Err(error);
}
}
}
}
}
{
let root_res = root_statements(input, context);
match root_res {
Ok((input, _)) => {
return Ok((input, ()));
},
Err(error) => {
match error {
Err::Incomplete(_) |
Err::Error(_) => {},
Err::Failure(_) => {
return Err(error);
}
}
}
}
}
{
let json_res = json_statements(input, context);
match json_res {
Ok((input, _)) => {
return Ok((input, ()));
},
Err(error) => {
match error {
Err::Incomplete(_) |
Err::Error(_) => {},
Err::Failure(_) => {
return Err(error);
}
}
}
}
}
if let Ok((input, docs)) = parse_inner_doc_comment(input) {
if context.profile.docs {
let self_ptr = context.self_ptr();
context.graph.insert_stof_data(&self_ptr, &nanoid!(15), Box::new(InnerDoc { docs }), None);
}
return Ok((input, ()));
}
if let Ok((input, _)) = whitespace_fail(input) {
return Ok((input, ()));
}
let (input, _) = eof(input)?;
Ok((input, ()))
}
fn root_statements<'a>(input: &'a str, context: &mut ParseContext) -> IResult<&'a str, (), StofParseError> {
let (input, name) = preceded(tag("root"), delimited(multispace0, opt(alt((map(ident, |s| s.to_owned()), double_string, single_string))), multispace0)).parse(input)?;
let (input, _) = char('{')(input)?;
let (mut input, custom_id) = opt(delimited(
space0,
delimited(char('('), take_until(")"), char(')')),
whitespace,
)).parse(input)?;
let mut cid = None;
if let Some(id) = custom_id { cid = Some(SId::from(id)); }
context.push_root(name, cid);
loop {
let res = document_statement(input, context);
match res {
Ok((rest, _)) => {
input = rest;
if input.starts_with('}') {
break;
}
},
Err(error) => {
return Err(error);
}
}
}
context.pop_self();
let (input, _) = char('}')(input)?;
Ok((input, ()))
}
fn json_statements<'a>(input: &'a str, context: &mut ParseContext) -> IResult<&'a str, (), StofParseError> {
let (mut input, _) = char('{')(input)?;
loop {
let res = document_statement(input, context);
match res {
Ok((rest, _)) => {
input = rest;
if input.starts_with('}') {
break;
}
},
Err(error) => {
return Err(error);
}
}
}
let (input, _) = char('}')(input)?;
Ok((input, ()))
}
#[cfg(test)]
mod tests {
use crate::{model::{Graph, Profile}, parser::{context::ParseContext, doc::document}, runtime::{Runtime, Val}};
#[test]
fn basic_doc() {
let mut graph = Graph::default();
{
let mut context = ParseContext::new(&mut graph, Profile::docs(true));
document(r#"
{
"max": 200
"object": {
"dude": true,
"hello": 450
}
list subobj: [
{
fn hello() -> str { 'hi' }
} as obj,
{
field: 'dude'
}
];
async fn another_yet(max: int = self.max) -> int {
let total = 0;
for (let i = 0; i < max; i += 1) total += 1;
total
}
fn main(x: float = 5) -> float {
let a = self.another_yet();
let b = self.another_yet(4000);
let c = self.another_yet(1000);
let d = self.another_yet(800);
(await a) + (await b) + (await c) + (await d)
}
}
"#, &mut context).unwrap();
}
graph.dump(true);
let res = Runtime::call(&mut graph, "root.main", vec![Val::from(10)]).unwrap();
assert_eq!(res, 6000.into());
}
}