commit 178ed4a952567ef9e17feb481e738decd0cf4deb Author: Alek Ratzloff Date: Tue Sep 1 17:32:48 2020 -0700 Initial commit Includes: runtime base from a previous project, syn(tax) module with parser and lexer Signed-off-by: Alek Ratzloff diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..102b236 --- /dev/null +++ b/build.rs @@ -0,0 +1,19 @@ +use cfgrammar::yacc::YaccKind; +use lrlex::LexerBuilder; +use lrpar::CTParserBuilder; + +fn main() -> Result<(), Box> { + let mut parser_builder = CTParserBuilder::new() + .yacckind(YaccKind::Grmtools) + .error_on_conflicts(false); + let lex_rule_ids_map = parser_builder + .process_file_in_src("syn/parser.y")?; + if let Some((grm, _, _, conflicts)) = parser_builder.conflicts() { + eprintln!("{}", conflicts.pp(grm)); + return Err("conflicts in parser".into()); + } + LexerBuilder::new() + .rule_ids_map(lex_rule_ids_map) + .process_file_in_src("syn/lexer.l")?; + Ok(()) +} diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml new file mode 100644 index 0000000..df4ca38 --- /dev/null +++ b/runtime/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "runtime" +version = "0.1.0" +authors = ["Alek Ratzloff "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +maplit = "1.0.2" +once_cell = "1.4.1" + +[dependencies.shredder] +git = "https://github.com/Others/shredder" +features = ["nightly-features"] diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs new file mode 100644 index 0000000..0e9fc6b --- /dev/null +++ b/runtime/src/lib.rs @@ -0,0 +1,4 @@ +#![feature(unsize, coerce_unsized)] + +#[macro_use] pub mod obj; +#[macro_use] pub mod vm; diff --git a/runtime/src/obj/attrs.rs b/runtime/src/obj/attrs.rs new file mode 100644 index 0000000..e14cd8a --- /dev/null +++ b/runtime/src/obj/attrs.rs @@ -0,0 +1,14 @@ +use crate::{obj::{Obj, ObjRef, sym::Sym}}; +use std::collections::BTreeMap; + +pub type Attrs = BTreeMap; + +impl Obj for Attrs { + fn attrs(&self) -> &Attrs { + self + } + + fn attrs_mut(&mut self) -> Option<&mut Attrs> { + Some(self) + } +} diff --git a/runtime/src/obj/fun.rs b/runtime/src/obj/fun.rs new file mode 100644 index 0000000..381c71e --- /dev/null +++ b/runtime/src/obj/fun.rs @@ -0,0 +1,111 @@ +use crate::{obj::{names::*, prelude::*}, vm::{inst::Inst, Vm}}; +use once_cell::sync::Lazy; +use shredder::{GcSafeWrapper, Scan}; +use std::fmt::{Debug, Formatter, self}; + +// +// struct UserFun +// +#[derive(Scan)] +pub struct UserFun { + attrs: Attrs, + // Safe because Vec doesn't need to be scanned + #[shredder(unsafe_skip)] + code: Vec, + // Safe because this is just an interner that points to symbols, which aren't GC'd + #[shredder(unsafe_skip)] + locals: Locals, +} + +impl UserFun { + pub fn code(&self) -> &Vec { + &self.code + } + + pub fn locals(&self) -> &Locals { + &self.locals + } +} + +impl_obj!(UserFun, attrs); + +impl Debug for UserFun { + fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { + fmt.debug_struct("UserFun") + .field("attrs", &self.attrs) + .field("code", &self.code) + .field("locals", &self.locals) + .finish() + } +} + +// +// struct NativeFun +// + +#[derive(Scan)] +pub struct NativeFun { + #[shredder(skip)] + fun: GcSafeWrapper>, + attrs: Attrs, +} + +// +// impl NativeFun +// + +impl NativeFun { + pub fn new(fun: Box) -> ObjRef { + let obj_ref = ObjRef::new(Self { + fun: GcSafeWrapper::new(fun), + attrs: Default::default(), + }); + + { + write_obj!(let obj = obj_ref); + obj.set_attr(*CALL_MEMBER_SYM, obj_ref.clone()); + obj.set_attr(*GET_ATTR_MEMBER_SYM, GET_ATTR_MEMBER_FUN.clone()); + } + + obj_ref + } + + pub fn call(&self, vm: &mut Vm) { + (self.fun)(vm) + } +} + +// +// impl Debug for NativeFun +// + +impl Debug for NativeFun { + fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { + fmt.debug_struct("NativeFun") + .field("fun", &format!("(function at {:x})", &self.fun as *const _ as usize)) + .field("attrs", &self.attrs) + .finish() + } +} + +impl_obj!(NativeFun, attrs); + +// +// Native function defs +// + +// __access__ is what the "dot" operator calls +// __get_attr__ *should* always bypass the __access__ function and get an attribute directly + +pub static GET_ATTR_MEMBER_FUN: Lazy> = Lazy::new(|| { + NativeFun::new(Box::new(|_vm| { + /* + * TODO - need SymObj or something like that, which can be used as an ObjRef - since that's + * all we'll have access to at runtime anyway + let sym_ref = vm.pop(); + let obj_ref = vm.pop(); + obj_ref.access() + */ + todo!("__get_attr__ function") + })) +}); diff --git a/runtime/src/obj/int.rs b/runtime/src/obj/int.rs new file mode 100644 index 0000000..c4fbb44 --- /dev/null +++ b/runtime/src/obj/int.rs @@ -0,0 +1,30 @@ +use crate::obj::{names::*, prelude::*}; +use shredder::Scan; + +#[derive(Debug, Scan)] +pub struct Int { + value: i64, + attrs: Attrs, +} + +impl Int { + pub fn new(value: i64) -> ObjRef { + let obj_ref = ObjRef::new(Self { + value, + attrs: Default::default(), + }); + + { + write_obj!(let obj = obj_ref); + obj.set_attr(*GET_ATTR_MEMBER_SYM, GET_ATTR_MEMBER_FUN.clone()); + } + + obj_ref + } + + pub fn value(&self) -> i64 { + self.value + } +} + +impl_obj!(Int, attrs); diff --git a/runtime/src/obj/intern.rs b/runtime/src/obj/intern.rs new file mode 100644 index 0000000..d8a873f --- /dev/null +++ b/runtime/src/obj/intern.rs @@ -0,0 +1,58 @@ +use crate::obj::sym::*; +use std::{collections::HashMap, hash::Hash, sync::Arc}; + +#[derive(Debug, Clone)] +pub struct Interner { + forward: Vec>, + reverse: HashMap, Sym>, +} + +impl Interner { + pub fn new(forward: Vec>) -> Self { + let reverse = forward.iter() + .enumerate() + .map(|(i, s)| (Arc::clone(s), Sym::new(i))) + .collect(); + Self { + forward, + reverse, + } + } + + pub fn forward(&self) -> &Vec> { + &self.forward + } + + pub fn reverse(&self) -> &HashMap, Sym> { + &self.reverse + } + + pub fn insert(&mut self, value: T) -> Sym { + if let Some(index) = self.reverse().get(&value) { + *index + } else { + let next_sym = Sym::new(self.forward().len()); + let ptr = Arc::new(value.into()); + self.forward.push(Arc::clone(&ptr)); + self.reverse.insert(ptr, next_sym); + assert_eq!(self.forward().len(), self.reverse().len()); + next_sym + } + } + + pub fn lookup(&self, sym: Sym) -> Option> { + self.forward.get(sym.index()) + .map(Arc::clone) + } + + pub fn lookup_sym(&self, value: impl std::borrow::Borrow) -> Option { + self.reverse.get(value.borrow()) + .copied() + } +} + +impl Default for Interner { + fn default() -> Self { + Interner { forward: Default::default(), reverse: Default::default(), } + } +} diff --git a/runtime/src/obj/macros.rs b/runtime/src/obj/macros.rs new file mode 100644 index 0000000..2466aa9 --- /dev/null +++ b/runtime/src/obj/macros.rs @@ -0,0 +1,45 @@ +/// Implements `Obj` for a given type and using the given member for attributes. +#[macro_export] +macro_rules! impl_obj { + ($ty:ty, $attrs:ident) => { + impl $crate::obj::Obj for $ty { + fn attrs(&self) -> &Attrs { + &self.$attrs + } + + fn attrs_mut(&mut self) -> Option<&mut Attrs> { + Some(&mut self.$attrs) + } + } + }; +} + +/// Locks a `ObjRef` type for reading. +#[macro_export] +macro_rules! read_obj { + (let $lhs:ident $(: &$ty:ty)? = $obj:expr) => { + let __obj $(: &std::sync::RwLock<$ty>)? = &*$obj.get(); + let $lhs $(: &$ty)? = &__obj.read().unwrap(); + }; + +} + +/// Locks a `ObjRef` type for writing. +#[macro_export] +macro_rules! write_obj { + (let $lhs:ident $(: &$ty:ty)? = $obj:expr) => { + let __obj $(: &std::sync::RwLock<$ty>)? = &*$obj.get(); + let $lhs $(: &$ty)? = &mut __obj.write().unwrap(); + }; +} + +#[macro_export] +macro_rules! attrs { + ($($key:expr => $value:expr),+ $(,)?) => {{ + maplit::btreemap! { $($key => ($value as _) ),+ } + }}; + + () => {{ + maplit::btreemap! { } + }} +} diff --git a/runtime/src/obj/mod.rs b/runtime/src/obj/mod.rs new file mode 100644 index 0000000..f01c7e6 --- /dev/null +++ b/runtime/src/obj/mod.rs @@ -0,0 +1,108 @@ +#[macro_use] +mod macros; + +pub mod attrs; +pub mod fun; +pub mod int; +pub mod intern; +pub mod names; +pub mod str; +pub mod sym; +pub mod ty; +#[cfg(test)] mod test; + +pub mod prelude { + pub use crate::obj::{attrs::*, fun::*, int::*, intern::*, str::*, sym::*, ty::*, Obj, ObjRef}; +} + +use shredder::{Gc, Scan}; +use std::{ + ops::{Deref, DerefMut, CoerceUnsized}, + marker::Unsize, + sync::RwLock, +}; + +use attrs::*; +use sym::Sym; + +// +// trait Obj +// + +pub trait Obj: Scan + std::fmt::Debug { + fn attrs(&self) -> &Attrs; + fn attrs_mut(&mut self) -> Option<&mut Attrs>; + + fn get_attr(&self, sym: &Sym) -> Option<&ObjRef> { + self.attrs().get(&sym) + } + + fn set_attr(&mut self, sym: Sym, value: ObjRef) -> Option { + self.attrs_mut()?.insert(sym, value) + } +} + +// +// struct ObjRef +// + +#[derive(Debug, Scan)] +pub struct ObjRef { + gc: Gc>, +} + +impl Clone for ObjRef { + fn clone(&self) -> Self { + ObjRef { gc: self.gc.clone() } + } +} + +// +// impl ObjRef +// + +impl ObjRef { + /// Check object reference equality. + pub fn ref_eq(&self, other: &Self) -> bool { + let lhs: &RwLock = &*self.gc.get(); + let rhs: &RwLock = &*other.get(); + std::ptr::eq::>(lhs, rhs) + } +} + +impl ObjRef { + pub fn new(obj: T) -> Self { + ObjRef { + gc: Gc::new(RwLock::new(obj)), + } + } +} + +impl CoerceUnsized> for ObjRef +where + T: Obj + Send + Sync + ?Sized + Unsize, + U: Obj + Send + Sync + ?Sized, +{ +} + +// +// impl Deref for ObjRef +// + +impl Deref for ObjRef { + type Target = Gc>; + + fn deref(&self) -> &Self::Target { + &self.gc + } +} + +// +// impl DerefMut for ObjRef +// + +impl DerefMut for ObjRef { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.gc + } +} diff --git a/runtime/src/obj/names.rs b/runtime/src/obj/names.rs new file mode 100644 index 0000000..6135453 --- /dev/null +++ b/runtime/src/obj/names.rs @@ -0,0 +1,55 @@ +use crate::obj::sym::{Sym, global_sym}; +use once_cell::sync::Lazy; + +macro_rules! name { + ($name:ident, $sym_name:ident, $text:expr $(,)?) => { + pub const $name: &str = $text; + pub static $sym_name: Lazy = Lazy::new(|| global_sym($name.to_string())); + } +} + +// +// Types +// +name!(INT_TY_NAME, INT_TY_SYM, "Int"); +name!(TY_TY_NAME, TY_TY_SYM, "Type"); +name!(SYM_TY_NAME, SYM_TY_SYM, "Sym"); + +// +// Members +// +name!(TY_MEMBER_NAME, TY_MEMBER_SYM, "__type__"); +name!(CALL_MEMBER_NAME, CALL_MEMBER_SYM, "__call__"); +name!(NAME_MEMBER_NAME, NAME_MEMBER_SYM, "__name__"); +name!(GET_ATTR_MEMBER_NAME, GET_ATTR_MEMBER_SYM, "__get_attr__"); +name!(SET_ATTR_MEMBER_NAME, SET_ATTR_MEMBER_SYM, "__set_attr__"); + +// +// Predefined VM-aware symbols +// +name!(LOCAL_NAME, LOCAL_SYM, "__local__"); + +// +// Builtin functions +// +//name!(REPR_FUN_NAME, REPR_FUN_SYM, "repr"); +//name!(GET_LOCAL_FUN_NAME, GET_LOCAL_FUN_SYM, "get_local"); +//name!(SET_LOCAL_FUN_NAME, SET_LOCAL_FUN_SYM, "set_local"); + +// +// Builtin constants +// +name!(TRUE_NAME, TRUE_SYM, "true"); +name!(FALSE_NAME, FALSE_SYM, "false"); +name!(NIL_NAME, NIL_SYM, "nil"); + +// Operator function names +name!(EQ_EQ_OP_NAME, EQ_EQ_OP_SYM, "__eq__"); +name!(LT_OP_NAME, LT_OP_SYM, "__lt__"); +name!(GT_OP_NAME, GT_OP_SYM, "__gt__"); +name!(LT_EQ_OP_NAME, LT_EQ_OP_SYM, "__le__"); +name!(GT_EQ_OP_NAME, GT_EQ_OP_SYM, "__ge__"); +name!(PLUS_OP_NAME, PLUS_OP_SYM, "__add__"); +name!(MINUS_OP_NAME, MINUS_OP_SYM, "__sub__"); +name!(TIMES_OP_NAME, TIMES_OP_SYM, "__mul__"); +name!(DIV_OP_NAME, DIV_OP_SYM, "__div__"); diff --git a/runtime/src/obj/str.rs b/runtime/src/obj/str.rs new file mode 100644 index 0000000..c61c462 --- /dev/null +++ b/runtime/src/obj/str.rs @@ -0,0 +1,11 @@ +use crate::obj::prelude::*; +use shredder::Scan; + +#[derive(Debug, Scan)] +pub struct Str { + value: String, + attrs: Attrs, +} + +impl_obj!(Str, attrs); + diff --git a/runtime/src/obj/sym.rs b/runtime/src/obj/sym.rs new file mode 100644 index 0000000..f95fcb5 --- /dev/null +++ b/runtime/src/obj/sym.rs @@ -0,0 +1,95 @@ +use crate::obj::{prelude::*, intern::Interner, names::*}; +use once_cell::sync::Lazy; +use shredder::Scan; +use std::sync::Mutex; + +// +// struct Sym +// + +/// A literal name or symbol. +#[derive(Scan, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Sym(usize); + +impl Sym { + pub fn new(sym: usize) -> Self { + Sym(sym) + } + + pub fn index(&self) -> usize { + self.0 + } + + pub fn new_obj(sym: impl Into) -> ObjRef { + ObjRef::new(sym.into()) + } +} + +impl From for Sym { + fn from(other: usize) -> Sym { + Sym(other) + } +} + +impl Obj for Sym { + fn attrs(&self) -> &Attrs { + &SYM_ATTRS + } + + fn attrs_mut(&mut self) -> Option<&mut Attrs> { None } +} + +// +// Symbol Ty object +// + +pub static NIL_OBJ: Lazy> = Lazy::new(|| Sym::new_obj(*NIL_SYM)); + +pub static TRUE_OBJ: Lazy> = Lazy::new(|| Sym::new_obj(*TRUE_SYM)); + +pub static FALSE_OBJ: Lazy> = Lazy::new(|| Sym::new_obj(*FALSE_SYM)); + +pub static SYM_TY: Lazy> = Lazy::new(|| Ty::new(SYM_TY_SYM_OBJ.clone())); + +/// The Sym type's name object (as a symbol). +pub static SYM_TY_SYM_OBJ: Lazy> = Lazy::new(|| Sym::new_obj(*SYM_TY_SYM)); + +pub static SYM_ATTRS: Lazy = Lazy::new(|| attrs! { + *TY_MEMBER_SYM => SYM_TY.clone(), + *NAME_MEMBER_SYM => SYM_TY_SYM_OBJ.clone(), +}); + +// +// global symbols table +// + +pub(crate) static SYMBOLS: Lazy> = Lazy::new(|| { + Mutex::new(SymTable::default()) +}); + +/* +pub(crate) fn global_name_lookup(sym: Sym) -> Option> { + let table = SYMBOLS.lock() + .unwrap(); + table.lookup(sym) +} + +pub(crate) fn global_sym_lookup(s: String) -> Option { + let table = SYMBOLS.lock() + .unwrap(); + table.lookup_sym(s) +} +*/ + +pub(crate) fn global_sym(s: String) -> Sym { + let mut table = SYMBOLS.lock() + .unwrap(); + table.insert(s) +} + +// +// SymTable +// + +pub type SymTable = Interner; +pub type Locals = Interner; diff --git a/runtime/src/obj/test.rs b/runtime/src/obj/test.rs new file mode 100644 index 0000000..0608c9a --- /dev/null +++ b/runtime/src/obj/test.rs @@ -0,0 +1,73 @@ +use crate::obj::{names::*, prelude::*}; +use once_cell::sync::Lazy; +use shredder::*; +use std::sync::Mutex; + +static TEST_LOCK: Lazy> = Lazy::new(|| Mutex::new(())); + +#[test] +fn test_sym_plumbing() { + // Most of this test is making sure we are free of runtime infinite recursion issues with + // initializing static data (NIL_OBJ, SYM_TY, SYM_ATTRS) + + let _guard = TEST_LOCK.lock().unwrap(); + let start = number_of_tracked_allocations(); // need 'start' because of static allocations + run_with_gc_cleanup(|| { + assert_eq!(number_of_tracked_allocations(), start + 0); + + let nil = NIL_OBJ.clone(); + + // nil sym obj + assert_eq!(number_of_tracked_allocations(), start + 1); + + { + read_obj!(let nil_obj = nil); + let sym: Sym = **nil_obj; + assert_eq!(*NIL_SYM, sym); + assert_eq!(number_of_tracked_allocations(), start + 1); + + // nil_obj.attrs will initialize: + // - SYM_ATTRS (not an object) + // + SYM_TY + // + SYM_TY_SYM_OBJ + // - SYM_TY_SYM (not an object) + // + TY_MEMBER_SYM + // + TY_TY + // - SYM_TY_SYM_OBJ (already initialized) + nil_obj.attrs(); + let ty_sym_obj = SYM_TY_SYM_OBJ.clone(); + assert_eq!(number_of_tracked_allocations(), start + 5); + } + + let on = TRUE_OBJ.clone(); + // true sym obj, sym ty obj shouldn't be duplicated + assert_eq!(number_of_tracked_allocations(), start + 6); + + let off = FALSE_OBJ.clone(); + // false sym obj, sym ty obj shouldn't be duplicated + assert_eq!(number_of_tracked_allocations(), start + 7); + }); + // these are *static* values, so there will always remain at least one reference. + assert_eq!(number_of_tracked_allocations(), start + 7); + // TODO ^ prove the above +} + +#[test] +fn test_dyn_obj_ref_eq() { + #[derive(Default, Debug, Scan)] + struct FooObj { attrs: Attrs } + + impl_obj!(FooObj, attrs); + + let _guard = TEST_LOCK.lock().unwrap(); + let start = number_of_tracked_allocations(); // need 'start' because of static allocations + run_with_gc_cleanup(|| { + let rf1: ObjRef = ObjRef::new(FooObj::default()); + let rf2 = rf1.clone(); + + assert!(rf1.ref_eq(&rf2)); + assert!(rf2.ref_eq(&rf1)); + assert_eq!(number_of_tracked_allocations(), start + 1); + }); + assert_eq!(number_of_tracked_allocations(), start + 0); +} diff --git a/runtime/src/obj/ty.rs b/runtime/src/obj/ty.rs new file mode 100644 index 0000000..a93361a --- /dev/null +++ b/runtime/src/obj/ty.rs @@ -0,0 +1,43 @@ +use crate::obj::{names::*, prelude::*}; +use once_cell::sync::Lazy; +use shredder::Scan; + +#[derive(Scan, Debug)] +pub struct Ty { + attrs: Attrs, +} + +impl Ty { + pub fn new(name: ObjRef) -> ObjRef { + // Ty objects have these attributes: + // __ty__ - always the Type type + // __name__ - this type's name as a symbol + ObjRef::new(Ty { + attrs: attrs! { + *TY_MEMBER_SYM => TY_TY.clone(), + *NAME_MEMBER_SYM => name.clone(), + } + }) + } +} + +pub static TY_TY_SYM_OBJ: Lazy> = Lazy::new(|| { + Sym::new_obj(*TY_TY_SYM) +}); + +pub static TY_TY: Lazy> = Lazy::new(|| { + let ty = ObjRef::new(Ty { + attrs: attrs! { + *NAME_MEMBER_SYM => TY_TY_SYM_OBJ.clone(), + } + }); + + // add self-reference + { + write_obj!(let ty_obj = ty); + ty_obj.set_attr(*TY_MEMBER_SYM, ty.clone()); + } + ty +}); + +impl_obj!(Ty, attrs); diff --git a/runtime/src/vm/frame.rs b/runtime/src/vm/frame.rs new file mode 100644 index 0000000..e7dbcf4 --- /dev/null +++ b/runtime/src/vm/frame.rs @@ -0,0 +1,6 @@ +/// A stack call frame. +#[derive(Default, Debug, Clone)] +pub struct Frame { + last_pc: usize, + stack_base: usize, +} diff --git a/runtime/src/vm/inst.rs b/runtime/src/vm/inst.rs new file mode 100644 index 0000000..1015f12 --- /dev/null +++ b/runtime/src/vm/inst.rs @@ -0,0 +1,41 @@ +use crate::obj::prelude::*; + +#[derive(Debug, PartialEq)] +pub enum Inst { + /// Push a literal symbol object to the stack. + PushSym(Sym), + + /// Pop a value from the stack, possibly into a local symbol. + Pop(Option), + + /// Pops a symbol value and an object reference. + /// + /// This will get an attr from the object reference pointed to by the symbol. + /// + /// If the object reference is `:__local__`, this instruction will push a local value. + GetAttr, + + /// Pops an object reference, a symbol value, and another object reference. + /// + /// This will set an attr of the second object ref to the first object ref, with the symbol + /// value name. + /// + /// If the given value is `:__local__`, this instruction will set a local value. + SetAttr, + + /// Jump to a given address in the current function unconditionally. + Jump(usize), + + /// Jump to a given address in the current function if the condition flag is true. + /// + /// The condition flag may be set by an internal function. + JumpTrue(usize), + + /// Pops the top item off of the stack, followed by the number of arguments supplied, and + /// attempts to run the `__call__` attribute of the object. + Call(usize), + + /// Pops the top value from the stack, and returns from the function, using the popped value as + /// a return value. + Return, +} diff --git a/runtime/src/vm/mod.rs b/runtime/src/vm/mod.rs new file mode 100644 index 0000000..7567f8a --- /dev/null +++ b/runtime/src/vm/mod.rs @@ -0,0 +1,51 @@ +mod frame; +pub mod inst; + +use crate::obj::prelude::*; + +use frame::*; + +#[derive(Debug)] +pub struct Vm { + stack: Vec, + frames: Vec, + pc: usize, + condition: bool, +} + +impl Vm { + pub fn new() -> Self { + Self { + stack: Default::default(), + frames: vec![Default::default()], // Start with a root stack frame + pc: 0, + condition: false, + } + } + + pub fn stack(&self) -> &Vec { + &self.stack + } + + pub fn stack_mut(&mut self) -> &mut Vec { + &mut self.stack + } + + pub fn push(&mut self, value: ObjRef) { + self.stack_mut().push(value); + } + + pub fn pop(&mut self) -> Option { + self.stack_mut().pop() + } + + pub fn pc(&self) -> usize { + self.pc + } + + pub fn condition(&self) -> bool { + self.condition + } + + //pub fn new_local(&mut self, name: String, +} diff --git a/src/bin/not.rs b/src/bin/not.rs new file mode 100644 index 0000000..7d672dd --- /dev/null +++ b/src/bin/not.rs @@ -0,0 +1,5 @@ +use not_python::syn; + +fn main() { + println!("hell world"); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..0afca50 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,3 @@ +pub mod syn; + +pub use runtime; diff --git a/src/syn/ast.rs b/src/syn/ast.rs new file mode 100644 index 0000000..9f08842 --- /dev/null +++ b/src/syn/ast.rs @@ -0,0 +1,162 @@ +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Stmt { + Expr(Expr), + Assign, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Expr { + Atom(Atom), + Bin(Box), + Un(Box), + Access(Box), +} + +impl From for Expr { + fn from(other: Atom) -> Self { + Expr::Atom(other) + } +} + +impl From for Expr { + fn from(other: BinExpr) -> Self { + Expr::Bin(other.into()) + } +} + +impl From for Expr { + fn from(other: UnExpr) -> Self { + Expr::Un(other.into()) + } +} + +impl From for Expr { + fn from(other: AccessExpr) -> Self { + Expr::Access(other.into()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum BinOp { + Plus, + Minus, + Times, + Div, + Eq, + Neq, + Lt, + Le, + Gt, + Ge, + And, + Or, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BinExpr { + pub lhs: Expr, + pub op: BinOp, + pub rhs: Expr, +} + +impl BinExpr { + pub fn new_expr(lhs: Expr, op: BinOp, rhs: Expr) -> Expr { + Self { lhs, op, rhs, }.into() + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum UnOp { + Plus, + Minus, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct UnExpr { + pub op: UnOp, + pub expr: Expr, +} + +impl UnExpr { + pub fn new_expr(op: UnOp, expr: Expr) -> Expr { + Self { op, expr }.into() + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct AccessExpr { + pub expr: Expr, + pub access: Vec, +} + +impl AccessExpr { + pub fn new_expr(expr: Expr, access: Vec) -> Expr { + Self { expr, access, }.into() + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Access { + pub access: String, + pub trailing: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ExprTrail { + Call(Vec), + Index(Expr), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Atom { + Ident(String), + Sym(String), + Num(i64), + String(String), +} + +pub fn parse(text: &str) -> Result>, Vec>> { + use crate::syn::{lexer, parser}; + let lexerdef = lexer::lexerdef(); + let lexer = lexerdef.lexer(text); + let (res, errors) = parser::parse(&lexer); + + if !errors.is_empty() { + return Err(errors) + } + + Ok(res.transpose().unwrap()) +} + +#[cfg(test)] +mod test { + use super::*; + + fn parse_expr(text: &str) -> Expr { + let mut body = parse(text) + .unwrap() + .unwrap(); + assert!(body.len() == 1); + let stmt = body.pop().unwrap(); + + if let Stmt::Expr(expr) = stmt { + expr + } else { + panic!("{:?} parses to {:?} which is not a Stmt::Expr", text, stmt); + } + } + + #[test] + fn access() { + assert_eq!( + parse_expr("a.b.c()"), + AccessExpr::new_expr( + Atom::Ident("a".to_string()).into(), + vec![ + Access { access: "b".to_string(), trailing: vec![], }, + Access { access: "c".to_string(), trailing: vec![ExprTrail::Call(vec![])], }, + ] + ) + ); + } +} diff --git a/src/syn/lexer.l b/src/syn/lexer.l new file mode 100644 index 0000000..75f1ce9 --- /dev/null +++ b/src/syn/lexer.l @@ -0,0 +1,27 @@ +%% +[\n;]+ "EOL" +[a-zA-Z_][a-zA-Z0-9_]* "IDENT" +:[a-zA-Z_][a-zA-Z0-9_]* "SYM" +[0-9]+ "NUM" +"([^"]|\\[rnt"'\\])+"|'([^"]|\\[rnt"'\\])+' "STRING" + +\|\| "||" +&& "&&" +< "<" +> ">" +<= "<=" +>= ">=" +!= "!=" +== "==" +\+ "+" +\* "*" +/ "/" +- "-" +\( "(" +\) ")" +\[ "[" +\] "]" +\. "." +, "," + +[\t ]+ ; diff --git a/src/syn/mod.rs b/src/syn/mod.rs new file mode 100644 index 0000000..ca9a41d --- /dev/null +++ b/src/syn/mod.rs @@ -0,0 +1,17 @@ +pub mod ast; + +pub mod lexer { + lrlex_mod!("syn/lexer.l"); + use lrlex::lrlex_mod; + pub use self::lexer_l::*; + +} + +pub mod parser { + lrpar_mod!("syn/parser.y"); + use lrpar::lrpar_mod; + pub use self::parser_y::*; +} + +//pub use lexer_l as lexer; +//pub use parser_y as parser; diff --git a/src/syn/parser.y b/src/syn/parser.y new file mode 100644 index 0000000..1ab1161 --- /dev/null +++ b/src/syn/parser.y @@ -0,0 +1,144 @@ +%start Body + +%left '||' +%left '&&' +%left '<' '>' '<=' '>=' '==' '!=' +%left '*' '/' +%left '+' '-' + +%% + +Body -> Result>: + Body 'EOL' Stmt { + flatten($1, $3) + } + | Stmt { Ok(vec![$1?]) } + | { Ok(Vec::new()) } + ; + +Stmt -> Result: + Expr { Ok(Stmt::Expr($1?)) } + ; + +Expr -> Result: BinExpr { $1 }; + +BinExpr -> Result: + UnExpr '||' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Or, $3?)) } + | UnExpr '&&' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::And, $3?)) } + | UnExpr '<' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Lt, $3?)) } + | UnExpr '>' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Gt, $3?)) } + | UnExpr '<=' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Le, $3?)) } + | UnExpr '>=' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Ge, $3?)) } + | UnExpr '==' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Eq, $3?)) } + | UnExpr '!=' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Neq, $3?)) } + | UnExpr '*' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Times, $3?)) } + | UnExpr '/' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Div, $3?)) } + | UnExpr '+' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Plus, $3?)) } + | UnExpr '-' BinExpr { Ok(BinExpr::new_expr($1?, BinOp::Plus, $3?)) } + | UnExpr { $1 } + ; + +UnExpr -> Result: + '+' UnExpr { Ok(UnExpr::new_expr(UnOp::Plus, $2?)) } + | '-' UnExpr { Ok(UnExpr::new_expr(UnOp::Minus, $2?)) } + | AccessExpr { $1 } + ; + +AccessExpr -> Result: + AtomExpr AccessExprTail { + Ok(AccessExpr::new_expr($1?, $2?)) + } + ; + +AccessExprTail -> Result>: + AccessExprTail '.' Ident ExprTrailing { + flatten($1, Ok(Access { access: $3?, trailing: $4? })) + } + | '.' Ident ExprTrailing { + Ok(vec![Access { access: $2?, trailing: $3? }]) + } + ; + +ExprTrailing -> Result>: + ExprTrailing '(' FunArgs ')' { flatten($1, Ok(ExprTrail::Call($3?))) } + | ExprTrailing '[' Expr ']' { flatten($1, Ok(ExprTrail::Index($3?))) } + | { Ok(Vec::new()) } + ; + +FunArgs -> Result>: + FunArgsTail { $1 } + | { Ok(Vec::new()) } + ; + +FunArgsTail -> Result>: + FunArgsTail ',' Expr { flatten($1, $3) } + | Expr { Ok(vec![$1?]) } + ; + +AtomExpr -> Result: + Atom { $1.map(Expr::Atom) } + | '(' Expr ')' { $2 } + ; + +Atom -> Result: + Ident { Ok(Atom::Ident($1?)) } + | 'SYM' { + let v = $1.map_err(|_| ())?; + let sym = &$lexer.span_str(v.span())[1..]; + Ok(Atom::Sym(sym.to_string())) + } + | 'NUM' { + let v = $1.map_err(|_| ())?; + let num_text = &$lexer.span_str(v.span()); + Ok(Atom::Num(num_text.parse().unwrap())) + } + | 'STRING' { + let v = $1.map_err(|_| ())?; + let string = &$lexer.span_str(v.span())[1..]; + Ok(Atom::String(parse_string(string))) + } + ; + +Ident -> Result: + 'IDENT' { + let v = $1.map_err(|_| ())?; + let ident = $lexer.span_str(v.span()).to_string(); + Ok(ident) + } + ; + +%% + +use crate::syn::ast::*; + +type Result = std::result::Result; + +fn flatten(head: Result>, tail: Result) -> Result> { + let mut head = head?; + let tail = tail?; + head.push(tail); + Ok(head) +} + +fn parse_string(input: &str) -> String { + let mut s = String::new(); + let input = &input[1..input.bytes().len() - 1]; + let mut chars = input.chars(); + while let Some(c) = chars.next() { + if c == '\\' { + let next = chars.next().unwrap(); + let c = match next { + '\\' => '\\', + '\'' => '\'', + '"' => '"', + 'n' => '\n', + 't' => '\t', + _ => unreachable!(), + }; + s.push(c); + } else { + s.push(c); + } + } + s +}