Skip to content

Commit a8c0d61

Browse files
committed
Minimal cli2 and std2
1 parent b4aa1c3 commit a8c0d61

27 files changed

Lines changed: 369 additions & 166 deletions

File tree

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ default-members = [
2323
[workspace.lints.rust]
2424
anonymous_parameters = "warn"
2525
bad_style = "warn"
26-
missing_docs = "warn"
27-
unused = "warn"
26+
missing_docs = "allow"
27+
unexpected_cfgs = "allow"
28+
unused = "allow"
2829
unused_extern_crates = "warn"
2930
unused_import_braces = "warn"
3031
unused_qualifications = "warn"

cli/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ thiserror = "1.0"
3131
version = "0.11.99" # ENDBASIC-VERSION
3232
path = "../client"
3333

34-
[dependencies.endbasic-core]
34+
[dependencies.endbasic-core2]
3535
version = "0.11.99" # ENDBASIC-VERSION
36-
path = "../core"
36+
path = "../core2"
3737

3838
[dependencies.endbasic-repl]
3939
version = "0.11.99" # ENDBASIC-VERSION

cli/src/main.rs

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,23 @@
1717
1818
use anyhow::{Result, anyhow};
1919
use async_channel::Sender;
20-
use endbasic_core::exec::Signal;
21-
use endbasic_std::console::{Console, ConsoleSpec};
22-
use endbasic_std::gpio;
23-
use endbasic_std::storage::Storage;
20+
#[cfg(not(feature = "crossterm"))]
21+
use async_channel::Sender;
22+
use endbasic_client::CloudService;
23+
use endbasic_core2::{Compiler, StopReason, Vm};
24+
use endbasic_repl::demos::DemoDriveFactory;
25+
#[cfg(not(feature = "crossterm"))]
26+
use endbasic_std::Signal;
27+
use endbasic_std::console::Console;
28+
#[cfg(feature = "rpi")]
29+
use endbasic_std::console::ConsoleSpec;
30+
#[cfg(not(feature = "sdl"))]
31+
use endbasic_std::console::ConsoleSpec;
32+
use endbasic_std::storage::{DirectoryDriveFactory, Storage};
33+
use endbasic_std::{InteractiveMachineBuilder, Machine, MachineBuilder, Signal, gpio};
2434
use getoptsargs::prelude::*;
2535
use std::cell::RefCell;
36+
use std::collections::HashMap;
2637
use std::fs::File;
2738
use std::io;
2839
use std::path::Path;
@@ -100,20 +111,20 @@ fn setup_gpio_pins(spec: Option<&str>) -> Result<Rc<RefCell<dyn gpio::Pins>>> {
100111
fn new_machine_builder(
101112
console_spec: Option<&str>,
102113
gpio_pins_spec: Option<&str>,
103-
) -> Result<endbasic_std::MachineBuilder> {
114+
) -> Result<MachineBuilder> {
104115
let signals_chan = async_channel::unbounded();
105-
let mut builder = endbasic_std::MachineBuilder::default();
116+
let mut builder = MachineBuilder::default();
106117
builder = builder.with_console(setup_console(console_spec, signals_chan.0.clone())?);
118+
/*
107119
builder = builder.with_signals_chan(signals_chan);
108120
builder = builder.with_gpio_pins(setup_gpio_pins(gpio_pins_spec)?);
121+
*/
109122
Ok(builder)
110123
}
111124

112125
/// Turns a regular machine builder into an interactive builder ensuring common features for all
113126
/// callers.
114-
fn make_interactive(
115-
builder: endbasic_std::MachineBuilder,
116-
) -> endbasic_std::InteractiveMachineBuilder {
127+
fn make_interactive(builder: MachineBuilder) -> InteractiveMachineBuilder {
117128
builder
118129
.make_interactive()
119130
.with_program(Rc::from(RefCell::from(endbasic_repl::editor::Editor::default())))
@@ -124,16 +135,16 @@ fn make_interactive(
124135
///
125136
/// `service_url` is the base URL of the cloud service.
126137
fn finish_interactive_build(
127-
mut builder: endbasic_std::InteractiveMachineBuilder,
138+
mut builder: InteractiveMachineBuilder,
128139
service_url: &str,
129-
) -> Result<endbasic_core::exec::Machine> {
140+
) -> Result<Machine> {
130141
let console = builder.get_console();
131142
let storage = builder.get_storage();
132143

133-
let mut machine = builder.build()?;
144+
let mut machine = builder.build();
134145

135-
let service = Rc::from(RefCell::from(endbasic_client::CloudService::new(service_url)?));
136-
endbasic_client::add_all(&mut machine, service, console, storage, "https://repl.endbasic.dev/");
146+
let service = Rc::from(RefCell::from(CloudService::new(service_url)?));
147+
//endbasic_client::add_all(&mut machine, service, console, storage, "https://repl.endbasic.dev/");
137148

138149
Ok(machine)
139150
}
@@ -244,12 +255,9 @@ fn setup_console(
244255
/// This instantiates non-optional drives, such as `MEMORY:` and `DEMOS:`, maps `LOCAL` the
245256
/// location given in `local_drive_spec`.
246257
pub fn setup_storage(storage: &mut Storage, local_drive_spec: &str) -> io::Result<()> {
247-
storage.register_scheme("demos", Box::from(endbasic_repl::demos::DemoDriveFactory::default()));
258+
storage.register_scheme("demos", Box::from(DemoDriveFactory::default()));
248259
storage.mount("demos", "demos://").expect("Demos drive shouldn't fail to mount");
249-
storage.register_scheme(
250-
"file",
251-
Box::from(endbasic_std::storage::DirectoryDriveFactory::default()),
252-
);
260+
storage.register_scheme("file", Box::from(DirectoryDriveFactory::default()));
253261
storage.mount("local", local_drive_spec)?;
254262
storage.cd("local:").expect("Local drive was just registered");
255263
Ok(())
@@ -286,9 +294,12 @@ async fn run_script<P: AsRef<Path>>(
286294
gpio_pins_spec: Option<&str>,
287295
) -> Result<i32> {
288296
let builder = new_machine_builder(console_spec, gpio_pins_spec)?;
289-
let mut machine = builder.build()?;
297+
let mut machine = builder.build();
290298
let mut input = File::open(path)?;
291-
Ok(machine.exec(&mut input).await?.as_exit_code())
299+
300+
machine.compile(&mut input)?;
301+
machine.exec().await?;
302+
Ok(0)
292303
}
293304

294305
/// Executes the `path` program in a fresh machine allowing any interactive-only calls.
@@ -331,7 +342,8 @@ async fn run_interactive(
331342
}
332343
None => {
333344
let mut input = File::open(path)?;
334-
Ok(machine.exec(&mut input).await?.as_exit_code())
345+
machine.compile(&mut input)?;
346+
Ok(machine.exec().await?.unwrap_or(0))
335347
}
336348
}
337349
}

client/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ serde_json = "1.0"
2323
time = { version = "0.3", features = ["std"] }
2424
url = "2.2"
2525

26-
[dependencies.endbasic-core]
26+
[dependencies.endbasic-core2]
2727
version = "0.11.99" # ENDBASIC-VERSION
28-
path = "../core"
28+
path = "../core2"
2929

3030
[dependencies.endbasic-std]
3131
version = "0.11.99" # ENDBASIC-VERSION

client/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ use std::io;
2222

2323
mod cloud;
2424
pub use cloud::CloudService;
25-
mod cmds;
26-
pub use cmds::add_all;
25+
//mod cmds;
26+
//pub use cmds::add_all;
2727
mod drive;
2828
pub(crate) use drive::CloudDriveFactory;
2929
#[cfg(test)]

core2/src/callable.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,27 @@ use crate::reader::LineCol;
2525
use async_trait::async_trait;
2626
use std::borrow::Cow;
2727
use std::fmt;
28+
use std::io;
2829
use std::ops::RangeInclusive;
2930
use std::rc::Rc;
3031
use std::str::Lines;
3132

3233
/// Error types for callable execution.
3334
#[derive(Debug, thiserror::Error)]
3435
pub enum CallError {
36+
/// I/O error.
37+
#[error("{0}")]
38+
IoError(#[from] io::Error),
39+
40+
/// Indicates to the caller that it must clear state.
41+
///
42+
/// This is a hack to support implementing `CLEAR` without explicit support in core.
43+
/// The need for this command is rare, so adding first-class support is unnecessary.
44+
/// Ideally, though, we could maybe change the `Vm` to support returning a `T` for
45+
/// callables instead of (), and then turn this into a proper std-owned enum.
46+
#[error("Machine requires state clearing")]
47+
NeedsClear,
48+
3549
/// Generic error with a static message.
3650
#[error("{0}")]
3751
Other(&'static str),

core2/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ mod vm;
3030
pub use ast::{ArgSep, ExprType};
3131
pub use bytecode::{ExitCode, InvalidExitCodeError, VarArgTag};
3232
pub use callable::*;
33-
pub use compiler::{Compiler, GlobalDef, GlobalDefKind, SymbolKey, only_metadata};
33+
pub use compiler::{Compiler, Error as CompilerError, GlobalDef, GlobalDefKind, SymbolKey};
3434
pub use mem::ConstantDatum;
35+
pub use reader::LineCol;
3536
pub use vm::{GetGlobalError, GetGlobalResult, StopReason, Vm};
3637

3738
#[cfg(test)]

core2/src/vm/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515

1616
//! Virtual machine for EndBASIC execution.
1717
18-
use crate::CallResult;
1918
use crate::ast::ExprType;
2019
use crate::bytecode::{ExitCode, Register};
2120
use crate::callable::{Callable, Scope};
2221
use crate::compiler::SymbolKey;
2322
use crate::image::Image;
2423
use crate::mem::{ConstantDatum, DatumPtr, HeapDatum};
2524
use crate::reader::LineCol;
25+
use crate::{CallError, CallResult};
2626
use std::collections::HashMap;
2727
use std::rc::Rc;
2828

@@ -65,6 +65,7 @@ impl<'a> UpcallHandler<'a> {
6565
let upcall = vm.upcalls[usize::from(index)].clone();
6666
match upcall.exec(vm.upcall_scope(first_reg, upcall_pc)).await {
6767
Ok(()) => Ok(()),
68+
e @ Err(CallError::NeedsClear) => e,
6869
Err(e) => {
6970
let pos_override = vm.image.as_ref().and_then(|image| {
7071
image.debug_info.instrs[upcall_pc].arg_linecols.first().copied()
@@ -137,7 +138,7 @@ impl Vm {
137138
}
138139

139140
/// Loads an `image` into the VM for execution, resetting any previous execution state.
140-
pub fn load(&mut self, image: Image) {
141+
pub fn reload(&mut self, image: Image) {
141142
self.upcalls.clear();
142143
for key in &image.upcalls {
143144
self.upcalls.push(
@@ -149,6 +150,11 @@ impl Vm {
149150
}
150151

151152
self.image = Some(image);
153+
}
154+
155+
/// Loads an `image` into the VM for execution, resetting any previous execution state.
156+
pub fn load(&mut self, image: Image) {
157+
self.reload(image);
152158

153159
self.heap.clear();
154160
self.context = Context::default();

repl/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ workspace = true
1818
async-trait = "0.1"
1919
time = { version = "0.3", features = ["std"] }
2020

21-
[dependencies.endbasic-core]
21+
[dependencies.endbasic-core2]
2222
version = "0.11.99" # ENDBASIC-VERSION
23-
path = "../core"
23+
path = "../core2"
2424

2525
[dependencies.endbasic-std]
2626
version = "0.11.99" # ENDBASIC-VERSION

repl/src/lib.rs

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515

1616
//! Interactive interpreter for the EndBASIC language.
1717
18-
use endbasic_core::exec::{Machine, StopReason};
18+
use endbasic_core2::{ExitCode, StopReason};
19+
use endbasic_std::Machine;
1920
use endbasic_std::console::{self, Console, is_narrow, refill_and_print};
2021
use endbasic_std::program::{BREAK_MSG, Program, continue_if_modified};
2122
use endbasic_std::storage::Storage;
@@ -63,8 +64,14 @@ pub async fn try_load_autoexec(
6364
}
6465
};
6566

66-
match machine.exec(&mut code.as_slice()).await {
67-
Ok(_) => Ok(()),
67+
match machine.compile(&mut code.as_slice()) {
68+
Ok(()) => match machine.exec().await {
69+
Ok(_) => Ok(()),
70+
Err(e) => {
71+
console.borrow_mut().print(&format!("AUTOEXEC.BAS failed: {}", e))?;
72+
Ok(())
73+
}
74+
},
6875
Err(e) => {
6976
console.borrow_mut().print(&format!("AUTOEXEC.BAS failed: {}", e))?;
7077
Ok(())
@@ -113,6 +120,7 @@ pub async fn run_from_cloud(
113120
console.borrow_mut().print("Starting...")?;
114121
console.borrow_mut().print("")?;
115122

123+
/*
116124
let result = machine.exec(&mut "RUN".as_bytes()).await;
117125
118126
let mut console = console.borrow_mut();
@@ -145,8 +153,8 @@ pub async fn run_from_cloud(
145153
[
146154
"You are now being dropped into the EndBASIC interpreter.",
147155
"The program you asked to run is still loaded in memory and you can interact with \
148-
it now. Use LIST to view the source code, EDIT to launch an editor on the source code, and RUN to \
149-
execute the program again.",
156+
it now. Use LIST to view the source code, EDIT to launch an editor on the source code, and RUN to \
157+
execute the program again.",
150158
"Type HELP for interactive usage information.",
151159
],
152160
" ",
@@ -155,6 +163,8 @@ execute the program again.",
155163
}
156164
157165
Ok(code)
166+
*/
167+
todo!();
158168
}
159169

160170
/// Enters the interactive interpreter.
@@ -166,9 +176,9 @@ pub async fn run_repl_loop(
166176
console: Rc<RefCell<dyn Console>>,
167177
program: Rc<RefCell<dyn Program>>,
168178
) -> io::Result<i32> {
169-
let mut stop_reason = StopReason::Eof;
179+
let mut stop_reason = None;
170180
let mut history = vec![];
171-
while stop_reason == StopReason::Eof {
181+
while stop_reason.is_none() {
172182
let line = {
173183
let mut console = console.borrow_mut();
174184
if console.is_interactive() {
@@ -177,13 +187,22 @@ pub async fn run_repl_loop(
177187
console::read_line(&mut *console, "", "", Some(&mut history)).await
178188
};
179189

190+
/*
180191
// Any signals entered during console input should not impact upcoming execution. Drain
181192
// them all.
182193
machine.drain_signals();
194+
*/
183195

184196
match line {
185-
Ok(line) => match machine.exec(&mut line.as_bytes()).await {
186-
Ok(reason) => stop_reason = reason,
197+
Ok(line) => match machine.compile(&mut line.as_bytes()) {
198+
Ok(()) => match machine.exec().await {
199+
Ok(None) => stop_reason = None,
200+
Ok(Some(code)) => stop_reason = Some(code),
201+
Err(e) => {
202+
let mut console = console.borrow_mut();
203+
console.print(format!("ERROR: {}", e).as_str())?;
204+
}
205+
},
187206
Err(e) => {
188207
let mut console = console.borrow_mut();
189208
console.print(format!("ERROR: {}", e).as_str())?;
@@ -199,13 +218,14 @@ pub async fn run_repl_loop(
199218
} else if e.kind() == io::ErrorKind::UnexpectedEof {
200219
let mut console = console.borrow_mut();
201220
console.print("End of input by CTRL-D")?;
202-
stop_reason = StopReason::Exited(0);
221+
stop_reason = Some(0);
203222
} else {
204-
stop_reason = StopReason::Exited(1);
223+
stop_reason = Some(1);
205224
}
206225
}
207226
}
208227

228+
/*
209229
match stop_reason {
210230
StopReason::Eof => (),
211231
StopReason::Break => {
@@ -219,8 +239,9 @@ pub async fn run_repl_loop(
219239
}
220240
}
221241
}
242+
*/
222243
}
223-
Ok(stop_reason.as_exit_code())
244+
Ok(stop_reason.unwrap())
224245
}
225246

226247
#[cfg(test)]

0 commit comments

Comments
 (0)