//! This file provides the Emacs binding for the core. #![allow(unused_imports)] extern crate repcore; use repcore::{ derive::stdag::{PFRecorder, STDAGParser}, grammar::{Grammar, GrammarInfo}, semiring::{IdentityRecorder, Nat, PFS}, tokenizer::to_tokenizer, valuation::{Count, ParseForest}, }; use repcore::parser::Parser as PParser; use repcore::parser::ParserReportConfig as PParserConfig; // TODO: Write Emacs binding // Let's design the API for Emacs to use. // // Emacs offers the grammar file or grammar string, and then we // generate a parser. // // Thereafter when we want to parse some input, Emacs gives the input // file name or the input file string to this package, and then the // package gives back the parse result. // // Besides, we need a data type to deal with errors. // // Handling a string is not a problem for us, but to return a parser // to Emacs, and to return the parse result, are kind of troublesome. // // To achieve this, we need data types for a parser and for the parse // result. Since a parser is just an implementation detail and should // not be of concern to the user, we mught just represent a parser as // just a custom user-ptr in Emacs. // // As to the parse result, we return a set of tuples: // // (label, i, k, j), // // where LABEL is represented as a tuple // // (INDEX, STRING), // // where index is the index of the rule and the string is the name of // the non-terminal or the terminal that is spanned by this packed // node. // // This is not intended for the user to inspect and play with. The // package should provide another function to interpret this data, for // example to search something or to display it. The reason we don't // directly return something to display is that the plain forest might // contain cyclic data, and is inconvenient to represent in Emacs. So // we only try to display this information to the user if the need // arises. #[derive(Debug, Clone)] #[repr(C)] pub struct Parser { g: Grammar, gi: GrammarInfo, stparser: STDAGParser, } impl Parser { fn new(gs: &str) -> Result> { let g: Grammar = gs.parse()?; let mut stparser = STDAGParser::new(); let mut gi = g.generate_info()?; stparser.init(&g, &mut gi)?; Ok(Self { g, gi, stparser }) } } #[no_mangle] extern "C" fn new_parser( gs: *mut std::os::raw::c_char, error: *mut std::os::raw::c_int, ) -> *mut Parser { let parsed_str; let parser_result; unsafe { let cstr = std::ffi::CStr::from_ptr(gs).to_str(); if cstr.is_err() { *error = 1; return std::ptr::null_mut(); } parsed_str = cstr.unwrap().to_owned(); parser_result = Parser::new(&parsed_str); if parser_result.is_err() { *error = 2; eprintln!("failed: {parser_result:?}"); return std::ptr::null_mut(); } *error = 0; } Box::into_raw(Box::new(parser_result.unwrap())) } #[no_mangle] extern "C" fn clean_parser(parser: *const std::ffi::c_void) { unsafe { Box::from_raw(parser as *mut Parser); } } // #[no_mangle] // extern "C" fn reset_parser(parser: *mut Parser) { // let mut parser_box; // unsafe { // parser_box = Box::from_raw(parser); // } // parser_box // .stparser // .init(&parser_box.g, &parser_box.gi) // .unwrap(); // std::mem::forget(parser_box); // } // FIXME: Use a pointer to int to indicate the type of errors, and use // a pointer to a custom error struct to convey the error messages. #[no_mangle] extern "C" fn parser_parse( parser: *mut Parser, file: *const std::os::raw::c_char, ) -> std::os::raw::c_int { let mut parser_box; let cstr; unsafe { parser_box = Box::from_raw(parser); cstr = std::ffi::CStr::from_ptr(file).to_str(); if cstr.is_err() { eprintln!("error reading string: {:?}", cstr); return 0; } } parser_box.stparser.reinit(); let file_string = std::fs::read_to_string(cstr.unwrap()); if file_string.is_err() { eprintln!("error reading file: {:?}", file_string); return 0; } let file_string = file_string.unwrap(); let parse_result = parser_box .stparser .parse(&file_string.chars().collect::>()); if parse_result.is_err() { eprintln!("failed to parse: {:?}", parse_result); return 0; } let (accepting, _) = parse_result.unwrap(); std::mem::forget(parser_box); accepting as std::os::raw::c_int } #[no_mangle] extern "C" fn parser_parse_string( parser: *mut Parser, input: *const std::os::raw::c_char, ) -> std::os::raw::c_int { let mut parser_box; let cstr; unsafe { parser_box = Box::from_raw(parser); cstr = std::ffi::CStr::from_ptr(input).to_str(); if cstr.is_err() { eprintln!("error reading string: {:?}", cstr); return 0; } } parser_box.stparser.reinit(); let file_string = cstr.unwrap().to_owned(); let parse_result = parser_box .stparser .parse(&file_string.chars().collect::>()); if parse_result.is_err() { eprintln!("failed to parse: {:?}", parse_result); return 0; } let (accepting, _) = parse_result.unwrap(); std::mem::forget(parser_box); accepting as std::os::raw::c_int }