1mod ast;
16mod ir;
17mod lexer;
18mod parser;
19mod program;
20
21mod sema;
22mod source;
23mod span;
24mod symbol;
25mod token;
26mod vm;
27
28use std::{borrow::Cow, fs, path::PathBuf};
29
30pub use ir::IrBuilder;
32pub use parser::Parser;
33pub use program::Program;
34
35use crate::ast::Expr;
36use crate::span::SpanError;
37use rust_decimal::Decimal;
38pub use sema::Sema;
39pub use source::Source;
40pub use symbol::{SymTable, Symbol, SymbolError};
41pub use vm::{Vm, VmError};
42
43#[derive(Debug)]
44enum EvalSource<'str> {
45 Source(Cow<'str, Source<'str>>),
46 File(PathBuf),
47}
48
49#[derive(Debug)]
50pub struct Eval<'str> {
51 source: EvalSource<'str>,
52 table: Option<SymTable>,
53}
54
55impl<'str> Eval<'str> {
56 pub fn new(string: &'str str) -> Self {
57 let source = Source::new(string);
58 Self {
59 source: EvalSource::Source(Cow::Owned(source)),
60 table: None,
61 }
62 }
63
64 pub fn new_from_source(source: &'str Source<'str>) -> Self {
65 Self {
66 source: EvalSource::Source(Cow::Borrowed(source)),
67 table: None,
68 }
69 }
70
71 pub fn new_from_file(path: PathBuf) -> Self {
72 Self {
73 source: EvalSource::File(path),
74 table: None,
75 }
76 }
77
78 pub fn with_table(&mut self, table: SymTable) {
79 self.table = Some(table);
80 }
81
82 pub fn run(&mut self) -> Result<Decimal, String> {
83 let program = self.build_program()?;
84 Vm::default().run(&program).map_err(|err| err.to_string())
85 }
86
87 pub fn compile_to_file(&mut self, path: &PathBuf) -> Result<(), String> {
88 let program = self.build_program()?;
89 let binary_data = program.compile().map_err(|err| err.to_string())?;
90 fs::write(path, binary_data).map_err(|err| err.to_string())
91 }
92
93 pub fn build_program(&mut self) -> Result<Program<'_>, String> {
94 let table = self.table.get_or_insert_with(SymTable::stdlib);
95 match &self.source {
96 EvalSource::Source(source) => {
97 let mut parser = Parser::new(source);
98 let mut ast: Expr = match parser
99 .parse()
100 .map_err(|err| Self::error_with_source(&err, source))?
101 {
102 Some(ast) => ast,
103 None => return Ok(Program::default()),
104 };
105 Sema::new(table)
106 .visit(&mut ast)
107 .map_err(|err| Self::error_with_source(&err, source))?;
108 IrBuilder::new().build(&ast).map_err(|err| err.to_string())
109 }
110 EvalSource::File(path) => {
111 let binary_data = fs::read(path).map_err(|err| err.to_string())?;
112 Program::load(&binary_data, table).map_err(|err| err.to_string())
113 }
114 }
115 }
116
117 fn error_with_source<T: SpanError>(error: &T, source: &Source) -> String {
118 format!("{}\n{}", error, source.highlight(&error.span()))
119 }
120}