DEV: Working Trace statements

This commit is contained in:
Jeremy Wall 2019-08-17 11:48:56 -05:00
parent 2985d4258f
commit 946a112eb4
5 changed files with 88 additions and 13 deletions

View File

@ -28,6 +28,8 @@ pub use vm::VM;
use pointer::OpPointer; use pointer::OpPointer;
use scope::Stack; use scope::Stack;
use crate::ast::Position;
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub enum Primitive { pub enum Primitive {
// Primitive Types // Primitive Types
@ -146,6 +148,7 @@ pub enum Hook {
Convert, Convert,
Regex, Regex,
Range, Range,
Trace(Position),
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]

View File

@ -12,9 +12,10 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::convert::TryFrom;
use std::convert::TryInto; use std::convert::TryInto;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::{Read, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::rc::Rc; use std::rc::Rc;
@ -24,12 +25,14 @@ use super::cache;
use super::Value::{C, F, P}; use super::Value::{C, F, P};
use super::VM; use super::VM;
use super::{Composite, Error, Hook, Primitive, Value}; use super::{Composite, Error, Hook, Primitive, Value};
use crate::ast::Position;
use crate::build::ir::Val;
use crate::build::AssertCollector; use crate::build::AssertCollector;
use crate::convert::{ConverterRegistry, ImporterRegistry}; use crate::convert::{ConverterRegistry, ImporterRegistry};
use Composite::{List, Tuple}; use Composite::{List, Tuple};
use Primitive::{Bool, Empty, Str, Int}; use Primitive::{Bool, Empty, Int, Str};
pub struct Builtins { pub struct Builtins<Out: Write, Err: Write> {
op_cache: cache::Ops, op_cache: cache::Ops,
val_cache: BTreeMap<String, Rc<Value>>, val_cache: BTreeMap<String, Rc<Value>>,
assert_results: AssertCollector, assert_results: AssertCollector,
@ -37,28 +40,40 @@ pub struct Builtins {
importer_registry: ImporterRegistry, importer_registry: ImporterRegistry,
working_dir: PathBuf, working_dir: PathBuf,
import_path: Vec<PathBuf>, import_path: Vec<PathBuf>,
// TODO(jwall): IO sink for stderr stdout: Out,
// TODO(jwall): IO sink for stdout stderr: Err,
} }
impl Builtins { type ByteSink = Vec<u8>;
pub fn new() -> Self {
Self::with_working_dir(std::env::current_dir().unwrap()) impl<Out: Write, Err: Write> Builtins<Out, Err> {
pub fn new(out: Out, err: Err) -> Self {
Self::with_working_dir(std::env::current_dir().unwrap(), out, err)
} }
pub fn with_working_dir<P: Into<PathBuf>>(path: P) -> Self { pub fn with_working_dir<P: Into<PathBuf>>(path: P, out: Out, err: Err) -> Self {
Self { Self {
op_cache: cache::Ops::new(), op_cache: cache::Ops::new(),
val_cache: BTreeMap::new(), val_cache: BTreeMap::new(),
assert_results: AssertCollector::new(), assert_results: AssertCollector::new(),
converter_registry: ConverterRegistry::make_registry(), converter_registry: ConverterRegistry::make_registry(),
importer_registry: ImporterRegistry::make_registry(), importer_registry: ImporterRegistry::make_registry(),
// TODO(jwall): This should move into the VM and not in the Runtime. // FIXME(jwall): This should move into the VM and not in the Runtime.
working_dir: path.into(), working_dir: path.into(),
import_path: Vec::new(), import_path: Vec::new(),
stdout: out,
stderr: err,
} }
} }
pub fn get_stdout(&self) -> &Out {
&self.stdout
}
pub fn get_stderr(&self) -> &Err {
&self.stderr
}
pub fn handle<P: AsRef<Path>>( pub fn handle<P: AsRef<Path>>(
&mut self, &mut self,
path: P, path: P,
@ -76,6 +91,7 @@ impl Builtins {
Hook::Reduce => self.reduce(path, stack), Hook::Reduce => self.reduce(path, stack),
Hook::Regex => self.regex(stack), Hook::Regex => self.regex(stack),
Hook::Range => self.range(stack), Hook::Range => self.range(stack),
Hook::Trace(pos) => self.trace(stack, pos),
} }
} }
@ -488,4 +504,30 @@ impl Builtins {
stack.push(Rc::new(C(List(elems)))); stack.push(Rc::new(C(List(elems))));
Ok(()) Ok(())
} }
fn trace(&mut self, mut stack: &mut Vec<Rc<Value>>, pos: Position) -> Result<(), Error> {
let val = if let Some(val) = dbg!(stack.pop()) {
val
} else {
return Err(dbg!(Error {}));
};
let expr = stack.pop();
let expr_pretty = match expr {
Some(ref expr) => match dbg!(expr.as_ref()) {
&P(Str(ref expr)) => expr.clone(),
_ => return Err(dbg!(Error {})),
},
_ => return Err(dbg!(Error {})),
};
let writable_val: Val = TryFrom::try_from(val.clone())?;
if let Err(_) = writeln!(
&mut self.stderr,
"TRACE: {} = {} at {}",
expr_pretty, writable_val, pos
) {
return Err(dbg!(Error {}));
};
stack.push(val);
Ok(())
}
} }

View File

@ -754,3 +754,19 @@ fn simple_selects() {
"select \"quux\", 3, { foo = 1, bar = 2, };" => P(Int(3)), "select \"quux\", 3, { foo = 1, bar = 2, };" => P(Int(3)),
]; ];
} }
#[test]
fn simple_trace() {
let stmts = parse(OffsetStrIter::from(dbg!("TRACE 1+1;")), None).unwrap();
let ops = Rc::new(translate::AST::translate(stmts));
assert!(ops.len() > 0);
let mut vm = VM::new("foo.ucg", ops.clone());
vm.run().unwrap();
assert_eq!(vm.pop().unwrap(), Rc::new(P(Int(2))));
let runtime = vm.get_runtime();
let err_out = runtime.get_stderr();
assert_eq!(
String::from_utf8_lossy(err_out).to_owned(),
"TRACE: 1 + 1 = 2 at line: 1 column: 1\n"
);
}

View File

@ -402,7 +402,15 @@ impl AST {
ops.push(Op::Cp); ops.push(Op::Cp);
} }
Expression::Debug(def) => { Expression::Debug(def) => {
unimplemented!("Debug expressions are not implmented yet"); let mut buffer: Vec<u8> = Vec::new();
{
let mut printer = crate::ast::printer::AstPrinter::new(2, &mut buffer);
let _ = printer.render_expr(&def.expr);
}
let expr_pretty = String::from_utf8(buffer).unwrap();
ops.push(Op::Val(Primitive::Str(expr_pretty)));
Self::translate_expr(*def.expr, &mut ops);
ops.push(Op::Runtime(Hook::Trace(def.pos)));
} }
} }
} }

View File

@ -11,6 +11,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use std::cell::Ref;
use std::cell::RefCell; use std::cell::RefCell;
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
@ -28,7 +29,8 @@ use super::{Func, Module};
pub struct VM { pub struct VM {
stack: Vec<Rc<Value>>, stack: Vec<Rc<Value>>,
symbols: Stack, symbols: Stack,
runtime: Rc<RefCell<runtime::Builtins>>, // FIXME(jwall): This should be parameterized.
runtime: Rc<RefCell<runtime::Builtins<Vec<u8>, Vec<u8>>>>,
ops: OpPointer, ops: OpPointer,
// TODO(jwall): This should be optional // TODO(jwall): This should be optional
path: PathBuf, path: PathBuf,
@ -43,12 +45,16 @@ impl<'a> VM {
Self { Self {
stack: Vec::new(), stack: Vec::new(),
symbols: Stack::new(), symbols: Stack::new(),
runtime: Rc::new(RefCell::new(runtime::Builtins::new())), runtime: Rc::new(RefCell::new(runtime::Builtins::new(Vec::new(), Vec::new()))),
ops: ops, ops: ops,
path: path.into(), path: path.into(),
} }
} }
pub fn get_runtime(&self) -> Ref<runtime::Builtins<Vec<u8>, Vec<u8>>> {
self.runtime.as_ref().borrow()
}
pub fn to_scoped(self, symbols: Stack) -> Self { pub fn to_scoped(self, symbols: Stack) -> Self {
Self { Self {
stack: Vec::new(), stack: Vec::new(),