refactor: type
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
use derive_more::Constructor;
|
||||
|
||||
use crate::vm::{Env, Symbol, Value, AttrSet};
|
||||
use crate::value::Const;
|
||||
use crate::vm::Env;
|
||||
use crate::ty::internal::{Const, Value, AttrSet, PrimOp};
|
||||
use crate::ty::common::Symbol;
|
||||
|
||||
pub fn env() -> Env {
|
||||
let mut env = Env::empty();
|
||||
@@ -44,65 +45,3 @@ pub fn env() -> Env {
|
||||
env
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Constructor)]
|
||||
pub struct PrimOp {
|
||||
name: &'static str,
|
||||
arity: u8,
|
||||
func: fn(Vec<Value>) -> Value,
|
||||
}
|
||||
|
||||
impl PartialEq for PrimOp {
|
||||
fn eq(&self, _: &Self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimOp {
|
||||
pub fn call(self, args: Vec<Value>) -> Value {
|
||||
if (args.len() as u8) < self.arity {
|
||||
Value::PartialPrimOp(PartialPrimOp {
|
||||
name: self.name,
|
||||
arity: self.arity - args.len() as u8,
|
||||
args,
|
||||
func: self.func,
|
||||
})
|
||||
} else if args.len() as u8 == self.arity {
|
||||
(self.func)(args)
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PartialPrimOp {
|
||||
name: &'static str,
|
||||
arity: u8,
|
||||
args: Vec<Value>,
|
||||
func: fn(Vec<Value>) -> Value,
|
||||
}
|
||||
|
||||
impl PartialEq for PartialPrimOp {
|
||||
fn eq(&self, _: &Self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialPrimOp {
|
||||
pub fn call(mut self, args: Vec<Value>) -> Value {
|
||||
let len = args.len() as u8;
|
||||
self.args.extend(args);
|
||||
if len < self.arity {
|
||||
Value::PartialPrimOp(PartialPrimOp {
|
||||
name: self.name,
|
||||
arity: self.arity - len,
|
||||
args: self.args,
|
||||
func: self.func,
|
||||
})
|
||||
} else if len == self.arity {
|
||||
(self.func)(self.args)
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::hash::Hash;
|
||||
|
||||
use ecow::EcoString;
|
||||
|
||||
use crate::value::Const;
|
||||
use crate::ty::internal::{Const, Param};
|
||||
|
||||
type Slice<T> = Box<[T]>;
|
||||
|
||||
@@ -12,17 +12,13 @@ pub type SymIdx = usize;
|
||||
pub type OpCodes = Slice<OpCode>;
|
||||
pub type Consts = Slice<Const>;
|
||||
pub type Thunks = Slice<Thunk>;
|
||||
pub type Args = Slice<Arg>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Thunk {
|
||||
pub opcodes: OpCodes,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub enum Arg {}
|
||||
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum OpCode {
|
||||
/// load a constant onto stack
|
||||
Const { value: Const },
|
||||
@@ -34,17 +30,25 @@ pub enum OpCode {
|
||||
LoadValue { idx: ThunkIdx },
|
||||
/// force TOS to value
|
||||
ForceValue,
|
||||
|
||||
/// [ .. func args @ .. ] consume (`arity` + 2) elements, call `func` with args` of length `arity`
|
||||
/// Example: __add 1 2 => [ LookUp("__add") Const(1) Const(2) Call(2) ]
|
||||
Call { arity: usize },
|
||||
/// return a value
|
||||
Ret,
|
||||
/// make a function
|
||||
Func { param: Param, length: usize },
|
||||
|
||||
/// consume 1 element, assert TOS is true
|
||||
Assert,
|
||||
|
||||
/// jump forward
|
||||
Jmp { step: usize },
|
||||
/// [ .. cond ] consume 1 element, if `cond`` is true, then jump forward
|
||||
JmpIfTrue { step: usize },
|
||||
/// [ .. cond ] consume 1 element, if `cond` is false, then jump forward
|
||||
JmpIfFalse { step: usize },
|
||||
|
||||
/// push an empty attribute set onto stack
|
||||
AttrSet,
|
||||
/// push an empty recursive attribute set onto stack
|
||||
@@ -53,10 +57,15 @@ pub enum OpCode {
|
||||
PushStaticAttr { name: EcoString },
|
||||
/// [ .. set name value ] consume 2 elements, push a dynamic kv pair (`name`, `value`) in to `set`
|
||||
PushDynamicAttr,
|
||||
|
||||
/// push an empty list onto stack
|
||||
List,
|
||||
/// [ .. list elem ] consume 1 element, push `elem` into `list`
|
||||
PushElem,
|
||||
|
||||
/// convert the string as TOS to a path
|
||||
Path,
|
||||
|
||||
/// [ .. a b ] consume 2 elements, perform a string concatenation `a` + `b`
|
||||
ConcatString,
|
||||
/// [ .. a b ] consume 2 elements, perform a binary operation `a` `op` `b`
|
||||
@@ -69,23 +78,19 @@ pub enum OpCode {
|
||||
HasDynamicAttr,
|
||||
/// [ .. set ] select `sym` from `set`
|
||||
Select { sym: EcoString },
|
||||
/// TODO:
|
||||
SelectWithDefault { sym: EcoString },
|
||||
/// TODO:
|
||||
SelectOrEmpty { sym: EcoString },
|
||||
/// TODO:
|
||||
/// [ .. set default ] select `sym` from `set` or `default`
|
||||
SelectOrDefault { sym: EcoString },
|
||||
/// [ .. set sym ] select `sym` from `set`
|
||||
SelectDynamic,
|
||||
/// TODO:
|
||||
SelectDynamicOrEmpty,
|
||||
/// TODO:
|
||||
SelectDynamicWithDefault,
|
||||
/// [ .. set sym default ] select `sym` from `set` or `default`
|
||||
|
||||
SelectDynamicOrDefault,
|
||||
/// enter the environment of the attribute set at TOS
|
||||
EnterEnv,
|
||||
/// exit current envrironment
|
||||
LeaveEnv,
|
||||
/// return a value
|
||||
Ret,
|
||||
/// no operation
|
||||
|
||||
/// no operation, used as termporary placeholder
|
||||
NoOp,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::bytecode::*;
|
||||
use crate::value::*;
|
||||
use crate::ty::internal::Const;
|
||||
|
||||
use super::ir;
|
||||
|
||||
@@ -275,23 +275,29 @@ impl Compile for ir::HasAttr {
|
||||
for attr in self.rhs {
|
||||
match attr {
|
||||
ir::Attr::Str(sym) => {
|
||||
comp.push(OpCode::SelectOrEmpty { sym });
|
||||
}
|
||||
comp.push(OpCode::AttrSet);
|
||||
comp.push(OpCode::SelectOrDefault { sym })
|
||||
},
|
||||
ir::Attr::Dynamic(dynamic) => {
|
||||
dynamic.compile(comp);
|
||||
comp.push(OpCode::SelectDynamicOrEmpty);
|
||||
comp.push(OpCode::AttrSet);
|
||||
comp.push(OpCode::SelectDynamicOrDefault);
|
||||
}
|
||||
ir::Attr::Strs(string) => {
|
||||
string.compile(comp);
|
||||
comp.push(OpCode::SelectDynamicOrEmpty);
|
||||
comp.push(OpCode::AttrSet);
|
||||
comp.push(OpCode::SelectDynamicOrDefault);
|
||||
}
|
||||
}
|
||||
}
|
||||
match comp.pop().unwrap() {
|
||||
OpCode::SelectOrEmpty { sym } => comp.push(OpCode::HasAttr { sym }),
|
||||
OpCode::SelectDynamicOrEmpty => comp.push(OpCode::HasDynamicAttr),
|
||||
let last = comp.pop().unwrap();
|
||||
let _ = comp.pop();
|
||||
match last {
|
||||
OpCode::SelectOrDefault { sym } => comp.push(OpCode::HasAttr { sym }),
|
||||
OpCode::SelectDynamicOrDefault => comp.push(OpCode::HasDynamicAttr),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,32 +306,42 @@ impl Compile for ir::Select {
|
||||
self.expr.compile(comp);
|
||||
for attr in self.attrpath {
|
||||
match attr {
|
||||
ir::Attr::Str(sym) => comp.push(OpCode::SelectOrEmpty { sym }),
|
||||
ir::Attr::Str(sym) => {
|
||||
comp.push(OpCode::AttrSet);
|
||||
comp.push(OpCode::SelectOrDefault { sym })
|
||||
},
|
||||
ir::Attr::Dynamic(dynamic) => {
|
||||
dynamic.compile(comp);
|
||||
comp.push(OpCode::SelectDynamicOrEmpty);
|
||||
comp.push(OpCode::AttrSet);
|
||||
comp.push(OpCode::SelectDynamicOrDefault);
|
||||
}
|
||||
ir::Attr::Strs(string) => {
|
||||
string.compile(comp);
|
||||
comp.push(OpCode::SelectDynamicOrEmpty);
|
||||
comp.push(OpCode::AttrSet);
|
||||
comp.push(OpCode::SelectDynamicOrDefault);
|
||||
}
|
||||
}
|
||||
}
|
||||
match self.default {
|
||||
Some(default) => {
|
||||
let last = comp.pop().unwrap();
|
||||
let _ = comp.pop();
|
||||
default.compile(comp);
|
||||
match last {
|
||||
OpCode::SelectOrEmpty { sym } => comp.push(OpCode::SelectWithDefault { sym }),
|
||||
OpCode::SelectDynamicOrEmpty => comp.push(OpCode::SelectDynamicWithDefault),
|
||||
OpCode::SelectOrDefault { sym } => comp.push(OpCode::SelectOrDefault { sym }),
|
||||
OpCode::SelectDynamicOrDefault => comp.push(OpCode::SelectDynamicOrDefault),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let last = comp.pop().unwrap();
|
||||
let _ = comp.pop();
|
||||
match last {
|
||||
OpCode::SelectOrDefault { sym } => comp.push(OpCode::Select { sym }),
|
||||
OpCode::SelectDynamicOrDefault => comp.push(OpCode::SelectDynamic),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
None => match comp.pop().unwrap() {
|
||||
OpCode::SelectOrEmpty { sym } => comp.push(OpCode::Select { sym }),
|
||||
OpCode::SelectDynamicOrEmpty => comp.push(OpCode::SelectDynamic),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -389,19 +405,28 @@ impl Compile for ir::Assert {
|
||||
}
|
||||
|
||||
impl Compile for ir::Func {
|
||||
fn compile(self, _comp: &mut Compiler) {
|
||||
todo!()
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
let idx = comp.idx();
|
||||
comp.push(OpCode::NoOp);
|
||||
let length = self.body.compile_with_length(comp);
|
||||
comp.modify(idx, OpCode::Func { param: self.param.into(), length });
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::Call {
|
||||
fn compile(self, _comp: &mut Compiler) {
|
||||
todo!()
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
let arity = self.args.len();
|
||||
self.func.compile(comp);
|
||||
self.args.into_iter().for_each(|arg| {
|
||||
arg.compile(comp);
|
||||
});
|
||||
comp.push(OpCode::Call { arity });
|
||||
}
|
||||
}
|
||||
|
||||
impl Compile for ir::Path {
|
||||
fn compile(self, _comp: &mut Compiler) {
|
||||
todo!()
|
||||
fn compile(self, comp: &mut Compiler) {
|
||||
self.expr.compile(comp);
|
||||
comp.push(OpCode::Path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use ecow::EcoString;
|
||||
use rnix::ast::{self, Expr};
|
||||
|
||||
use crate::bytecode::{ConstIdx, Consts, ThunkIdx};
|
||||
use crate::value::Const as Const_;
|
||||
use crate::ty::internal as i;
|
||||
|
||||
use super::compile::*;
|
||||
use super::env::IrEnv;
|
||||
@@ -116,14 +116,14 @@ ir! {
|
||||
UnOp => { rhs: Box<Ir>, kind: UnOpKind },
|
||||
Select => { expr: Box<Ir>, attrpath: Vec<Attr>, default: Option<Box<Ir>> },
|
||||
If => { cond: Box<Ir>, consq: Box<Ir>, alter: Box<Ir> },
|
||||
Func => { args: Vec<Param>, body: Box<Ir> },
|
||||
Func => { param: Param, body: Box<Ir> },
|
||||
Call => { func: Box<Ir>, args: Vec<Ir> },
|
||||
|
||||
Let => { attrs: Attrs, expr: Box<Ir> },
|
||||
With => { namespace: Box<Ir>, expr: Box<Ir> },
|
||||
Assert => { assertion: Box<Ir>, expr: Box<Ir> },
|
||||
ConcatStrings => { parts: Vec<Ir> },
|
||||
Const => { value: Const_ },
|
||||
Const => { value: i::Const },
|
||||
Var => { sym: EcoString },
|
||||
#[derive(Copy)]
|
||||
Thunk => { idx: ThunkIdx },
|
||||
@@ -163,8 +163,8 @@ pub struct DowngradeError {
|
||||
pub struct DowngradeState {
|
||||
envs: Vec<Env>,
|
||||
thunks: Vec<Ir>,
|
||||
consts: Vec<Const_>,
|
||||
consts_table: HashMap<Const_, ConstIdx>,
|
||||
consts: Vec<i::Const>,
|
||||
consts_table: HashMap<i::Const, ConstIdx>,
|
||||
}
|
||||
|
||||
pub struct Downgraded {
|
||||
@@ -355,12 +355,33 @@ impl From<ast::UnaryOpKind> for UnOpKind {
|
||||
pub enum Param {
|
||||
Ident(EcoString),
|
||||
Formals {
|
||||
formals: Vec<(EcoString, Option<Ir>)>,
|
||||
formals: Vec<(EcoString, Option<Thunk>)>,
|
||||
ellipsis: bool,
|
||||
alias: Option<EcoString>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Into<i::Param> for Param {
|
||||
fn into(self) -> i::Param {
|
||||
use i::Param::*;
|
||||
match self {
|
||||
Param::Ident(ident) => Ident(ident),
|
||||
Param::Formals {
|
||||
formals,
|
||||
ellipsis,
|
||||
alias,
|
||||
} => Formals {
|
||||
formals: formals
|
||||
.into_iter()
|
||||
.map(|(ident, default)| (ident, default.map(|thunk| thunk.idx)))
|
||||
.collect(),
|
||||
ellipsis,
|
||||
alias,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait Downgrade
|
||||
where
|
||||
Self: Sized,
|
||||
@@ -607,14 +628,10 @@ impl Downgrade for ast::With {
|
||||
|
||||
impl Downgrade for ast::Lambda {
|
||||
fn downgrade(self, state: &mut DowngradeState) -> Result<Ir> {
|
||||
let mut body = self.body().unwrap();
|
||||
let mut args = vec![downgrade_param(self.param().unwrap(), state)?];
|
||||
while let ast::Expr::Lambda(func) = body {
|
||||
body = func.body().unwrap();
|
||||
args.push(downgrade_param(func.param().unwrap(), state)?);
|
||||
}
|
||||
let body = self.body().unwrap();
|
||||
let param = downgrade_param(self.param().unwrap(), state)?;
|
||||
let body = body.downgrade(state)?.boxed();
|
||||
Func { args, body }.ir().ok()
|
||||
Func { param, body }.ir().ok()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -651,7 +668,7 @@ fn downgrade_pattern(pattern: ast::Pattern, state: &mut DowngradeState) -> Resul
|
||||
.default()
|
||||
.unwrap()
|
||||
.downgrade(state)
|
||||
.map(|ok| (ident, Some(ok)))
|
||||
.map(|ok| (ident, Some(state.new_thunk(ok))))
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
@@ -4,5 +4,5 @@ mod builtins;
|
||||
mod bytecode;
|
||||
mod compile;
|
||||
mod downcast;
|
||||
mod value;
|
||||
mod ty;
|
||||
mod vm;
|
||||
|
||||
26
src/ty/common.rs
Normal file
26
src/ty/common.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use std::ops::Deref;
|
||||
|
||||
use ecow::EcoString;
|
||||
use derive_more::Constructor;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Constructor)]
|
||||
pub struct Catchable {
|
||||
msg: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, Constructor)]
|
||||
pub struct Symbol(EcoString);
|
||||
|
||||
impl<T: Into<EcoString>> From<T> for Symbol {
|
||||
fn from(value: T) -> Self {
|
||||
Symbol(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Symbol {
|
||||
type Target = str;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use derive_more::Constructor;
|
||||
use rpds::HashTrieMapSync;
|
||||
|
||||
use crate::value::{self, Value as Value_};
|
||||
use super::super::public as p;
|
||||
use super::super::common::Symbol;
|
||||
|
||||
use super::super::vm::VM;
|
||||
use super::{Symbol, ToValue, Value};
|
||||
use crate::vm::VM;
|
||||
use super::{ToPublic, Value};
|
||||
|
||||
#[derive(Debug, Constructor, Clone, PartialEq)]
|
||||
pub struct AttrSet {
|
||||
@@ -47,15 +48,15 @@ impl AttrSet {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToValue for AttrSet {
|
||||
fn to_value(self, vm: &VM) -> Value_ {
|
||||
Value_::AttrSet(value::AttrSet::new(
|
||||
impl ToPublic for AttrSet {
|
||||
fn to_public(self, vm: &VM) -> p::Value {
|
||||
p::Value::AttrSet(p::AttrSet::new(
|
||||
self.data
|
||||
.iter()
|
||||
.map(|(sym, value)| {
|
||||
(
|
||||
value::Symbol::new(sym.0.clone()),
|
||||
value.clone().to_value(vm),
|
||||
sym.clone(),
|
||||
value.clone().to_public(vm),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
@@ -110,7 +110,7 @@ impl PartialEq for Const {
|
||||
|
||||
impl Eq for Const {}
|
||||
|
||||
impl Hash for Const {
|
||||
/* impl Hash for Const {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
use Const::*;
|
||||
match self {
|
||||
@@ -121,4 +121,4 @@ impl Hash for Const {
|
||||
Func(func) => func.hash(state),
|
||||
}
|
||||
}
|
||||
}
|
||||
} */
|
||||
25
src/ty/internal/func.rs
Normal file
25
src/ty/internal/func.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use ecow::EcoString;
|
||||
|
||||
use crate::bytecode::{OpCodes, ThunkIdx};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Param {
|
||||
Ident(EcoString),
|
||||
Formals {
|
||||
formals: Vec<(EcoString, Option<ThunkIdx>)>,
|
||||
ellipsis: bool,
|
||||
alias: Option<EcoString>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Func {
|
||||
pub param: Param,
|
||||
pub opcodes: OpCodes
|
||||
}
|
||||
|
||||
impl PartialEq for Func {
|
||||
fn eq(&self, _: &Self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
use derive_more::Constructor;
|
||||
use rpds::VectorSync;
|
||||
|
||||
use crate::value::{self, Value as Value_};
|
||||
use crate::ty::public as p;
|
||||
|
||||
use super::super::vm::VM;
|
||||
use super::{ToValue, Value};
|
||||
use crate::vm::VM;
|
||||
use super::{ToPublic, Value};
|
||||
|
||||
#[derive(Debug, Constructor, Clone, PartialEq)]
|
||||
pub struct List {
|
||||
@@ -30,12 +30,12 @@ impl List {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToValue for List {
|
||||
fn to_value(self, vm: &VM) -> Value_ {
|
||||
Value_::List(value::List::new(
|
||||
impl ToPublic for List {
|
||||
fn to_public(self, vm: &VM) -> p::Value {
|
||||
p::Value::List(p::List::new(
|
||||
self.data
|
||||
.iter()
|
||||
.map(|value| value.clone().to_value(vm))
|
||||
.map(|value| value.clone().to_public(vm))
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
@@ -1,31 +1,30 @@
|
||||
use anyhow::Result;
|
||||
use derive_more::{Constructor, IsVariant, Unwrap};
|
||||
use ecow::EcoString;
|
||||
|
||||
use crate::value::{Value as Value_, Const, Catchable};
|
||||
use super::public as p;
|
||||
use super::common as c;
|
||||
|
||||
use super::env::Env;
|
||||
use super::vm::VM;
|
||||
use c::Symbol;
|
||||
|
||||
use crate::vm::Env;
|
||||
use crate::vm::VM;
|
||||
|
||||
mod attrset;
|
||||
mod list;
|
||||
mod string;
|
||||
mod func;
|
||||
mod cnst;
|
||||
mod primop;
|
||||
|
||||
pub use attrset::AttrSet;
|
||||
pub use list::List;
|
||||
pub use string::ContextfulString;
|
||||
pub use func::*;
|
||||
pub use primop::*;
|
||||
pub use cnst::Const;
|
||||
|
||||
pub trait ToValue {
|
||||
fn to_value(self, vm: &VM) -> Value_;
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Constructor)]
|
||||
pub struct Symbol(EcoString);
|
||||
|
||||
impl<T: Into<EcoString>> From<T> for Symbol {
|
||||
fn from(value: T) -> Self {
|
||||
Symbol(value.into())
|
||||
}
|
||||
pub trait ToPublic {
|
||||
fn to_public(self, vm: &VM) -> p::Value;
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Constructor)]
|
||||
@@ -37,9 +36,9 @@ pub enum Value {
|
||||
Thunk(Thunk),
|
||||
AttrSet(AttrSet),
|
||||
List(List),
|
||||
Catchable(crate::value::Catchable),
|
||||
PrimOp(crate::builtins::PrimOp),
|
||||
PartialPrimOp(crate::builtins::PartialPrimOp),
|
||||
Catchable(c::Catchable),
|
||||
PrimOp(PrimOp),
|
||||
PartialPrimOp(PartialPrimOp),
|
||||
}
|
||||
|
||||
use Value::Const as VmConst;
|
||||
@@ -191,7 +190,7 @@ impl Value {
|
||||
if let Value::AttrSet(attrs) = self {
|
||||
let val = attrs
|
||||
.select(sym.clone())
|
||||
.unwrap_or(Value::Catchable(Catchable::new(Some(format!(
|
||||
.unwrap_or(Value::Catchable(c::Catchable::new(Some(format!(
|
||||
"{sym:?} not found"
|
||||
)))));
|
||||
*self = val;
|
||||
@@ -239,16 +238,16 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToValue for Value {
|
||||
fn to_value(self, vm: &VM) -> Value_ {
|
||||
impl ToPublic for Value {
|
||||
fn to_public(self, vm: &VM) -> p::Value {
|
||||
match self {
|
||||
Value::AttrSet(attrs) => attrs.to_value(vm),
|
||||
Value::List(list) => list.to_value(vm),
|
||||
Value::Catchable(catchable) => Value_::Catchable(catchable),
|
||||
Value::Const(cnst) => Value_::Const(cnst),
|
||||
Value::Thunk(_) => Value_::Thunk,
|
||||
Value::PrimOp(_) => Value_::PrimOp,
|
||||
Value::PartialPrimOp(_) => Value_::PartialPrimOp,
|
||||
Value::AttrSet(attrs) => attrs.to_public(vm),
|
||||
Value::List(list) => list.to_public(vm),
|
||||
Value::Catchable(catchable) => p::Value::Catchable(catchable),
|
||||
Value::Const(cnst) => p::Value::Const(cnst.into()),
|
||||
Value::Thunk(_) => p::Value::Thunk,
|
||||
Value::PrimOp(_) => p::Value::PrimOp,
|
||||
Value::PartialPrimOp(_) => p::Value::PartialPrimOp,
|
||||
}
|
||||
}
|
||||
}
|
||||
67
src/ty/internal/primop.rs
Normal file
67
src/ty/internal/primop.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use derive_more::Constructor;
|
||||
|
||||
use super::Value;
|
||||
|
||||
#[derive(Debug, Clone, Constructor)]
|
||||
pub struct PrimOp {
|
||||
pub name: &'static str,
|
||||
arity: u8,
|
||||
func: fn(Vec<Value>) -> Value,
|
||||
}
|
||||
|
||||
impl PartialEq for PrimOp {
|
||||
fn eq(&self, _: &Self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimOp {
|
||||
pub fn call(self, args: Vec<Value>) -> Value {
|
||||
if (args.len() as u8) < self.arity {
|
||||
Value::PartialPrimOp(PartialPrimOp {
|
||||
name: self.name,
|
||||
arity: self.arity - args.len() as u8,
|
||||
args,
|
||||
func: self.func,
|
||||
})
|
||||
} else if args.len() as u8 == self.arity {
|
||||
(self.func)(args)
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PartialPrimOp {
|
||||
pub name: &'static str,
|
||||
arity: u8,
|
||||
args: Vec<Value>,
|
||||
func: fn(Vec<Value>) -> Value,
|
||||
}
|
||||
|
||||
impl PartialEq for PartialPrimOp {
|
||||
fn eq(&self, _: &Self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialPrimOp {
|
||||
pub fn call(mut self, args: Vec<Value>) -> Value {
|
||||
let len = args.len() as u8;
|
||||
self.args.extend(args);
|
||||
if len < self.arity {
|
||||
Value::PartialPrimOp(PartialPrimOp {
|
||||
name: self.name,
|
||||
arity: self.arity - len,
|
||||
args: self.args,
|
||||
func: self.func,
|
||||
})
|
||||
} else if len == self.arity {
|
||||
(self.func)(self.args)
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
src/ty/mod.rs
Normal file
3
src/ty/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod public;
|
||||
pub mod internal;
|
||||
pub mod common;
|
||||
136
src/ty/public/cnst.rs
Normal file
136
src/ty/public/cnst.rs
Normal file
@@ -0,0 +1,136 @@
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use anyhow::Error;
|
||||
use derive_more::{IsVariant, Unwrap};
|
||||
use ecow::EcoString;
|
||||
|
||||
use super::super::internal as i;
|
||||
|
||||
#[derive(Debug, Clone, IsVariant, Unwrap)]
|
||||
pub enum Const {
|
||||
Bool(bool),
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
String(EcoString),
|
||||
Func
|
||||
}
|
||||
|
||||
impl From<i::Const> for Const {
|
||||
fn from(value: i::Const) -> Self {
|
||||
use i::Const::*;
|
||||
match value {
|
||||
Bool(bool) => Const::Bool(bool),
|
||||
Int(int) => Const::Int(int),
|
||||
Float(float) => Const::Float(float),
|
||||
String(string) => Const::String(string),
|
||||
Func(func) => Const::Func
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Const {
|
||||
fn from(value: bool) -> Self {
|
||||
Const::Bool(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for Const {
|
||||
fn from(value: i64) -> Self {
|
||||
Const::Int(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Const {
|
||||
fn from(value: f64) -> Self {
|
||||
Const::Float(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EcoString> for Const {
|
||||
fn from(value: EcoString) -> Self {
|
||||
Const::String(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Const {
|
||||
fn from(value: String) -> Self {
|
||||
Const::String(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Const {
|
||||
fn from(value: &str) -> Self {
|
||||
Const::String(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a Const> for &'a bool {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Const::Bool(b) => Ok(b),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'a> TryFrom<&'a Const> for &'a i64 {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Const::Int(int) => Ok(int),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a Const> for &'a f64 {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Const::Float(float) => Ok(float),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a Const> for &'a str {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: &'a Const) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Const::String(string) => Ok(string),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Const {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
use Const::*;
|
||||
match (self, other) {
|
||||
(Bool(a), Bool(b)) => a == b,
|
||||
(Int(a), Int(b)) => a == b,
|
||||
(Float(a), Float(b)) => a == b,
|
||||
(String(a), String(b)) => a == b,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Const {}
|
||||
|
||||
impl Hash for Const {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
use Const::*;
|
||||
match self {
|
||||
Bool(b) => b.hash(state),
|
||||
Int(int) => int.hash(state),
|
||||
Float(float) => float.to_bits().hash(state),
|
||||
String(string) => string.hash(state),
|
||||
func @ Func => func.hash(state),
|
||||
}
|
||||
}
|
||||
}
|
||||
43
src/ty/public/mod.rs
Normal file
43
src/ty/public/mod.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use std::fmt::{Debug, Formatter, Result as FmtResult};
|
||||
|
||||
use derive_more::{Constructor, IsVariant, Unwrap};
|
||||
use rpds::{HashTrieMapSync, VectorSync};
|
||||
|
||||
use super::common::*;
|
||||
|
||||
mod cnst;
|
||||
|
||||
pub use cnst::Const;
|
||||
|
||||
#[derive(Constructor, Clone, PartialEq)]
|
||||
pub struct AttrSet {
|
||||
data: HashTrieMapSync<Symbol, Value>,
|
||||
}
|
||||
|
||||
impl Debug for AttrSet {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
write!(f, "{{ ")?;
|
||||
for (k, v) in self.data.iter() {
|
||||
write!(f, "{k:?} = {v:?}; ")?;
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Constructor, Clone, Debug, PartialEq)]
|
||||
pub struct List {
|
||||
data: VectorSync<Value>,
|
||||
}
|
||||
|
||||
|
||||
#[derive(IsVariant, Unwrap, Clone, Debug, PartialEq)]
|
||||
pub enum Value {
|
||||
Const(Const),
|
||||
AttrSet(AttrSet),
|
||||
List(List),
|
||||
Catchable(Catchable),
|
||||
Thunk,
|
||||
Func,
|
||||
PrimOp,
|
||||
PartialPrimOp,
|
||||
}
|
||||
111
src/value/mod.rs
111
src/value/mod.rs
@@ -1,111 +0,0 @@
|
||||
use std::fmt::{Debug, Formatter, Result as FmtResult};
|
||||
use std::ops::Deref;
|
||||
|
||||
use derive_more::{Constructor, IsVariant, Unwrap};
|
||||
use ecow::EcoString;
|
||||
use rpds::{HashTrieMapSync, VectorSync};
|
||||
|
||||
use crate::bytecode::{Args, OpCodes};
|
||||
|
||||
mod cnst;
|
||||
|
||||
pub use cnst::Const;
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, Constructor)]
|
||||
pub struct Symbol(EcoString);
|
||||
|
||||
impl<T: Into<EcoString>> From<T> for Symbol {
|
||||
fn from(value: T) -> Self {
|
||||
Symbol(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Symbol {
|
||||
type Target = str;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct Func {
|
||||
pub args: Args,
|
||||
pub opcodes: OpCodes,
|
||||
}
|
||||
|
||||
impl PartialEq for Func {
|
||||
fn eq(&self, _: &Self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/* #[derive(IsVariant, Unwrap, Clone, Debug, PartialEq)]
|
||||
pub enum Const {
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
Bool(bool),
|
||||
String(EcoString),
|
||||
Func(Arc<Func>),
|
||||
}
|
||||
|
||||
impl Display for Const {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
write!(f, "Const::")?;
|
||||
match self {
|
||||
Const::Int(int) => write!(f, "Int@{}", int),
|
||||
Const::Float(float) => write!(f, "Float@{}", float),
|
||||
Const::Bool(bool) => write!(f, "Bool@{}", bool),
|
||||
Const::String(string) => write!(f, r#"String@"{}""#, string.as_ref()),
|
||||
Const::Func(func) => write!(f, "Func@{:?}", func.as_ref() as *const Func),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ByteCodeConst> for Const {
|
||||
fn from(value: ByteCodeConst) -> Self {
|
||||
use ByteCodeConst::*;
|
||||
match value {
|
||||
Int(int) => Const::Int(int),
|
||||
Float(float) => Const::Float(float),
|
||||
Bool(bool) => Const::Bool(bool),
|
||||
String(string) => Const::String(EcoString::from(string)),
|
||||
Func(func) => Const::Func(Arc::new(func)),
|
||||
}
|
||||
}
|
||||
} */
|
||||
|
||||
#[derive(Constructor, Clone, PartialEq)]
|
||||
pub struct AttrSet {
|
||||
data: HashTrieMapSync<Symbol, Value>,
|
||||
}
|
||||
|
||||
impl Debug for AttrSet {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
write!(f, "{{ ")?;
|
||||
for (k, v) in self.data.iter() {
|
||||
write!(f, "{k:?} = {v:?}; ")?;
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Constructor, Clone, Debug, PartialEq)]
|
||||
pub struct List {
|
||||
data: VectorSync<Value>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Constructor)]
|
||||
pub struct Catchable {
|
||||
msg: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(IsVariant, Unwrap, Clone, Debug, PartialEq)]
|
||||
pub enum Value {
|
||||
Const(Const),
|
||||
AttrSet(AttrSet),
|
||||
List(List),
|
||||
Catchable(Catchable),
|
||||
Thunk,
|
||||
PrimOp,
|
||||
PartialPrimOp,
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
use rpds::HashTrieMapSync;
|
||||
|
||||
use super::value::{Symbol, Value};
|
||||
use crate::ty::common::Symbol;
|
||||
use crate::ty::internal::Value;
|
||||
|
||||
pub struct Env {
|
||||
last: Option<Box<Env>>,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
mod env;
|
||||
mod stack;
|
||||
mod value;
|
||||
mod vm;
|
||||
mod vmthunk;
|
||||
|
||||
@@ -8,5 +7,4 @@ mod vmthunk;
|
||||
mod test;
|
||||
|
||||
pub use env::Env;
|
||||
pub use value::Symbol;
|
||||
pub use value::*;
|
||||
pub use vm::VM;
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::ops::Deref;
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
|
||||
use super::value::Value;
|
||||
use crate::ty::internal::Value;
|
||||
|
||||
pub const STACK_SIZE: usize = 8 * 1024 / size_of::<Value>();
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@ use ecow::EcoString;
|
||||
use rpds::{ht_map_sync, vector_sync};
|
||||
|
||||
use crate::compile::compile;
|
||||
use crate::value::*;
|
||||
use crate::ty::public::*;
|
||||
use crate::ty::common::Symbol;
|
||||
|
||||
use super::vm::run;
|
||||
|
||||
@@ -166,3 +167,8 @@ fn test_let() {
|
||||
attrs! { symbol!("a") => attrs!{ symbol!("ac") => int!(2) } },
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_func() {
|
||||
test_expr("(x: x) 1", int!(1));
|
||||
}
|
||||
|
||||
36
src/vm/vm.rs
36
src/vm/vm.rs
@@ -1,17 +1,18 @@
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::builtins::env;
|
||||
use crate::bytecode::{self, *};
|
||||
use crate::value::{Const, Value as Value_};
|
||||
use crate::bytecode::{self, Program, OpCode, OpCodes, Thunks, UnOp, BinOp};
|
||||
use crate::ty::internal::*;
|
||||
use crate::ty::common::Symbol;
|
||||
use crate::ty::public as p;
|
||||
|
||||
use super::env::Env;
|
||||
use super::stack::{STACK_SIZE, Stack};
|
||||
use super::value::{self as vmValue, *};
|
||||
use super::vmthunk::*;
|
||||
|
||||
pub fn run(prog: Program) -> Result<Value_> {
|
||||
pub fn run(prog: Program) -> Result<p::Value> {
|
||||
let vm = VM::new(prog.thunks);
|
||||
Ok(vm.eval(prog.top_level, &mut env())?.to_value(&vm))
|
||||
Ok(vm.eval(prog.top_level, &mut env())?.to_public(&vm))
|
||||
}
|
||||
|
||||
pub struct VM {
|
||||
@@ -54,7 +55,7 @@ impl VM {
|
||||
match opcode {
|
||||
OpCode::NoOp => (),
|
||||
OpCode::Const { value } => stack.push(Value::Const(value))?,
|
||||
OpCode::LoadThunk { idx } => stack.push(Value::Thunk(vmValue::Thunk::new(idx)))?,
|
||||
OpCode::LoadThunk { idx } => stack.push(Value::Thunk(Thunk::new(idx)))?,
|
||||
OpCode::LoadValue { idx } => {
|
||||
stack.push(self.get_thunk_value(idx, env)?)?;
|
||||
}
|
||||
@@ -128,17 +129,11 @@ impl VM {
|
||||
OpCode::Select { sym } => {
|
||||
stack.tos_mut()?.select(Symbol::new(sym)).force(self, env)?;
|
||||
}
|
||||
OpCode::SelectWithDefault { sym } => {
|
||||
OpCode::SelectOrDefault { sym } => {
|
||||
let default = stack.pop()?;
|
||||
stack
|
||||
.tos_mut()?
|
||||
.select_with_default(Symbol::new(sym), default.clone());
|
||||
}
|
||||
OpCode::SelectOrEmpty { sym } => {
|
||||
stack.tos_mut()?.select_with_default(
|
||||
Symbol::new(sym),
|
||||
Value::AttrSet(AttrSet::empty()),
|
||||
);
|
||||
.select_with_default(Symbol::new(sym), default);
|
||||
}
|
||||
OpCode::SelectDynamic => {
|
||||
let mut val = stack.pop().unwrap();
|
||||
@@ -146,21 +141,12 @@ impl VM {
|
||||
let sym = val.unwrap_const().unwrap_string().into();
|
||||
stack.tos_mut()?.select(sym);
|
||||
}
|
||||
OpCode::SelectDynamicWithDefault => {
|
||||
let mut val = stack.pop().unwrap();
|
||||
val.coerce_to_string();
|
||||
let sym = val.unwrap_const().unwrap_string().into();
|
||||
OpCode::SelectDynamicOrDefault => {
|
||||
let default = stack.pop()?;
|
||||
stack.tos_mut()?.select_with_default(sym, default.clone());
|
||||
}
|
||||
OpCode::SelectDynamicOrEmpty => {
|
||||
let mut val = stack.pop().unwrap();
|
||||
val.coerce_to_string();
|
||||
let sym = val.unwrap_const().unwrap_string().into();
|
||||
stack.tos_mut()?.select_with_default(
|
||||
sym,
|
||||
Value::AttrSet(AttrSet::empty()),
|
||||
);
|
||||
stack.tos_mut()?.select_with_default(sym, default);
|
||||
}
|
||||
OpCode::HasAttr { sym } => {
|
||||
stack.tos_mut()?.has_attr(Symbol::new(sym));
|
||||
|
||||
@@ -7,7 +7,7 @@ use derive_more::{IsVariant, Unwrap};
|
||||
use crate::bytecode::OpCodes;
|
||||
|
||||
use super::env::Env;
|
||||
use super::value::Value;
|
||||
use crate::ty::internal::Value;
|
||||
use super::vm::VM;
|
||||
|
||||
pub struct VmThunk {
|
||||
|
||||
Reference in New Issue
Block a user