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 scope::Stack;
use crate::ast::Position;
#[derive(Debug, PartialEq, Clone)]
pub enum Primitive {
// Primitive Types
@ -146,6 +148,7 @@ pub enum Hook {
Convert,
Regex,
Range,
Trace(Position),
}
#[derive(Debug, PartialEq, Clone)]

View File

@ -12,9 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::collections::BTreeMap;
use std::convert::TryFrom;
use std::convert::TryInto;
use std::fs::File;
use std::io::Read;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::rc::Rc;
@ -24,12 +25,14 @@ use super::cache;
use super::Value::{C, F, P};
use super::VM;
use super::{Composite, Error, Hook, Primitive, Value};
use crate::ast::Position;
use crate::build::ir::Val;
use crate::build::AssertCollector;
use crate::convert::{ConverterRegistry, ImporterRegistry};
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,
val_cache: BTreeMap<String, Rc<Value>>,
assert_results: AssertCollector,
@ -37,28 +40,40 @@ pub struct Builtins {
importer_registry: ImporterRegistry,
working_dir: PathBuf,
import_path: Vec<PathBuf>,
// TODO(jwall): IO sink for stderr
// TODO(jwall): IO sink for stdout
stdout: Out,
stderr: Err,
}
impl Builtins {
pub fn new() -> Self {
Self::with_working_dir(std::env::current_dir().unwrap())
type ByteSink = Vec<u8>;
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 {
op_cache: cache::Ops::new(),
val_cache: BTreeMap::new(),
assert_results: AssertCollector::new(),
converter_registry: ConverterRegistry::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(),
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>>(
&mut self,
path: P,
@ -76,6 +91,7 @@ impl Builtins {
Hook::Reduce => self.reduce(path, stack),
Hook::Regex => self.regex(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))));
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)),
];
}
#[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);
}
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.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::cell::Ref;
use std::cell::RefCell;
use std::path::PathBuf;
use std::rc::Rc;
@ -28,7 +29,8 @@ use super::{Func, Module};
pub struct VM {
stack: Vec<Rc<Value>>,
symbols: Stack,
runtime: Rc<RefCell<runtime::Builtins>>,
// FIXME(jwall): This should be parameterized.
runtime: Rc<RefCell<runtime::Builtins<Vec<u8>, Vec<u8>>>>,
ops: OpPointer,
// TODO(jwall): This should be optional
path: PathBuf,
@ -43,12 +45,16 @@ impl<'a> VM {
Self {
stack: Vec::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,
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 {
Self {
stack: Vec::new(),