aplang_lib/
aplang.rs

1use crate::interpreter::errors::RuntimeError;
2use crate::interpreter::FunctionMap;
3use crate::interpreter::Interpreter;
4use crate::interpreter::Value;
5use crate::lexer::token::Token;
6use crate::lexer::Lexer;
7use crate::parser::ast::pretty::TreePrinter;
8use crate::parser::ast::Ast;
9use crate::parser::Parser;
10use miette::Report;
11use std::fmt::Write;
12use std::marker::PhantomData;
13use std::path::PathBuf;
14use std::sync::Arc;
15use std::{fmt, fs, io};
16
17pub struct Initialized;
18pub struct Lexed;
19pub struct Parsed;
20pub struct Executed;
21pub struct ExecutedWithDebug;
22
23pub struct ApLang<State = Initialized> {
24    source_code: Arc<str>,
25    file_path: Option<PathBuf>,
26
27    tokens: Option<Vec<Token>>, // generated with the lexer
28    ast: Option<Ast>,           // generated with the parser
29    values: Option<Vec<Value>>,
30
31    _state: PhantomData<State>,
32}
33
34impl ApLang {
35    pub fn new_from_file(file_path: PathBuf) -> io::Result<Self> {
36        // check if the file exists
37        let source_code: Arc<str> = fs::read_to_string(file_path.clone())?.into();
38
39        Ok(Self {
40            source_code,
41            file_path: Some(file_path),
42
43            tokens: None,
44            ast: None,
45            values: None,
46
47            _state: PhantomData,
48        })
49    }
50
51    pub fn new_from_stdin(source_code: impl Into<Arc<str>>) -> Self {
52        ApLang {
53            source_code: source_code.into(),
54            file_path: Some(PathBuf::default()),
55            tokens: None,
56            ast: None,
57            values: None,
58
59            _state: PhantomData,
60        }
61    }
62
63    // dont use this
64    pub fn new(source_code: impl Into<Arc<str>>, file_path: Option<PathBuf>) -> Self {
65        Self {
66            source_code: source_code.into(),
67            file_path,
68
69            tokens: None,
70            ast: None,
71            values: None,
72
73            _state: PhantomData,
74        }
75    }
76
77    pub fn get_source_code(&self) -> Arc<str> {
78        self.source_code.clone()
79    }
80}
81
82impl ApLang<Initialized> {
83    /// executes the lexer to convert source code into tokens
84    pub fn lex(self) -> Result<ApLang<Lexed>, Vec<Report>> {
85        let file_name = self
86            .file_path
87            .clone()
88            .unwrap()
89            .file_name()
90            .map(|name| name.to_string_lossy().into_owned())
91            .unwrap_or_default();
92
93        let tokens = Lexer::scan(self.source_code.clone(), file_name)?;
94
95        // move the data into the next state struct
96        Ok(ApLang {
97            source_code: self.source_code,
98            file_path: self.file_path,
99            tokens: Some(tokens), // tokens now exist
100            ast: None,
101            values: None,
102
103            _state: PhantomData,
104        })
105    }
106}
107
108impl ApLang<Lexed> {
109    pub fn parse(self) -> Result<ApLang<Parsed>, Vec<Report>> {
110        // we know that tokens exist
111        let tokens = unsafe { self.tokens.unwrap_unchecked() };
112
113        let file_name = self
114            .file_path
115            .clone()
116            .unwrap()
117            .file_name()
118            .map(|name| name.to_string_lossy().into_owned())
119            .unwrap_or_default();
120
121        let mut parser = Parser::new(tokens, Arc::clone(&self.source_code), file_name.as_str());
122
123        let ast = parser.parse()?;
124
125        Ok(ApLang {
126            source_code: self.source_code,
127            file_path: self.file_path,
128            tokens: None,
129            ast: Some(ast),
130            values: None,
131
132            _state: PhantomData,
133        })
134    }
135
136    pub fn debug_output<Writer: Write>(&self, buf: &mut Writer) -> fmt::Result {
137        for token in unsafe { self.tokens.as_ref().unwrap_unchecked() } {
138            if token.is_soft_semi() {
139                write!(buf, " ;")?;
140            }
141
142            write!(buf, "{}", token)?;
143        }
144
145        Ok(())
146    }
147}
148
149impl ApLang<Parsed> {
150    pub fn execute_as_module(self) -> Result<FunctionMap, RuntimeError> {
151        Interpreter::new(unsafe { self.ast.unwrap_unchecked() }, self.file_path).interpret_module()
152    }
153
154    pub fn execute(self) -> Result<ApLang<Executed>, Report> {
155        Interpreter::new(
156            unsafe { self.ast.unwrap_unchecked() },
157            self.file_path.clone(),
158        )
159        .interpret()
160        .map_err(|err| {
161            let named_source = err.named_source.clone();
162            Report::from(err).with_source_code(named_source)
163        })?;
164
165        Ok(ApLang {
166            source_code: self.source_code,
167            file_path: self.file_path,
168            tokens: None,
169            ast: None,
170            values: None,
171
172            _state: PhantomData,
173        })
174    }
175
176    pub fn execute_with_debug(self) -> Result<ApLang<ExecutedWithDebug>, Report> {
177        let ast = unsafe { self.ast.unwrap_unchecked() };
178        let mut interpreter = Interpreter::new(ast, self.file_path.clone());
179        let values = interpreter.interpret_debug().map_err(|err| {
180            let named_source = err.named_source.clone();
181            Report::from(err).with_source_code(named_source)
182        })?;
183
184        Ok(ApLang {
185            source_code: self.source_code,
186            file_path: self.file_path,
187            tokens: None,
188            ast: None,
189            values: Some(values),
190
191            _state: PhantomData,
192        })
193    }
194
195    pub fn debug_output<Writer: Write>(&self, buf: &mut Writer) -> fmt::Result {
196        let ast = unsafe { self.ast.as_ref().unwrap_unchecked() };
197
198        write!(buf, "{}", ast.print_tree())
199    }
200}
201
202impl ApLang<ExecutedWithDebug> {
203    pub fn debug_output<Writer: Write>(&self, buf: &mut Writer) -> fmt::Result {
204        let values = unsafe { self.values.as_ref().unwrap_unchecked() };
205
206        for value in values {
207            writeln!(buf, "EXPR OUTPUT: {}", value)?;
208        }
209
210        Ok(())
211    }
212}