Initial commit
Includes: runtime base from a previous project, syn(tax) module with parser and lexer Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
Cargo.lock
|
||||||
19
build.rs
Normal file
19
build.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
use cfgrammar::yacc::YaccKind;
|
||||||
|
use lrlex::LexerBuilder;
|
||||||
|
use lrpar::CTParserBuilder;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
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(())
|
||||||
|
}
|
||||||
15
runtime/Cargo.toml
Normal file
15
runtime/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "runtime"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Alek Ratzloff <alekratz@gmail.com>"]
|
||||||
|
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"]
|
||||||
4
runtime/src/lib.rs
Normal file
4
runtime/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#![feature(unsize, coerce_unsized)]
|
||||||
|
|
||||||
|
#[macro_use] pub mod obj;
|
||||||
|
#[macro_use] pub mod vm;
|
||||||
14
runtime/src/obj/attrs.rs
Normal file
14
runtime/src/obj/attrs.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
use crate::{obj::{Obj, ObjRef, sym::Sym}};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
pub type Attrs = BTreeMap<Sym, ObjRef>;
|
||||||
|
|
||||||
|
impl Obj for Attrs {
|
||||||
|
fn attrs(&self) -> &Attrs {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn attrs_mut(&mut self) -> Option<&mut Attrs> {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
111
runtime/src/obj/fun.rs
Normal file
111
runtime/src/obj/fun.rs
Normal file
@@ -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<Inst> doesn't need to be scanned
|
||||||
|
#[shredder(unsafe_skip)]
|
||||||
|
code: Vec<Inst>,
|
||||||
|
// 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<Inst> {
|
||||||
|
&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<Box<dyn Fn(&mut Vm) + Send + Sync>>,
|
||||||
|
attrs: Attrs,
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// impl NativeFun
|
||||||
|
//
|
||||||
|
|
||||||
|
impl NativeFun {
|
||||||
|
pub fn new(fun: Box<dyn Fn(&mut Vm) + Send + Sync>) -> ObjRef<Self> {
|
||||||
|
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<ObjRef<NativeFun>> = 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")
|
||||||
|
}))
|
||||||
|
});
|
||||||
30
runtime/src/obj/int.rs
Normal file
30
runtime/src/obj/int.rs
Normal file
@@ -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<Self> {
|
||||||
|
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);
|
||||||
58
runtime/src/obj/intern.rs
Normal file
58
runtime/src/obj/intern.rs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
use crate::obj::sym::*;
|
||||||
|
use std::{collections::HashMap, hash::Hash, sync::Arc};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Interner<T: Hash + Eq> {
|
||||||
|
forward: Vec<Arc<T>>,
|
||||||
|
reverse: HashMap<Arc<T>, Sym>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Hash + Eq> Interner<T> {
|
||||||
|
pub fn new(forward: Vec<Arc<T>>) -> Self {
|
||||||
|
let reverse = forward.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, s)| (Arc::clone(s), Sym::new(i)))
|
||||||
|
.collect();
|
||||||
|
Self {
|
||||||
|
forward,
|
||||||
|
reverse,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn forward(&self) -> &Vec<Arc<T>> {
|
||||||
|
&self.forward
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reverse(&self) -> &HashMap<Arc<T>, 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<Arc<T>> {
|
||||||
|
self.forward.get(sym.index())
|
||||||
|
.map(Arc::clone)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup_sym(&self, value: impl std::borrow::Borrow<T>) -> Option<Sym> {
|
||||||
|
self.reverse.get(value.borrow())
|
||||||
|
.copied()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Hash + Eq> Default for Interner<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Interner { forward: Default::default(), reverse: Default::default(), }
|
||||||
|
}
|
||||||
|
}
|
||||||
45
runtime/src/obj/macros.rs
Normal file
45
runtime/src/obj/macros.rs
Normal file
@@ -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! { }
|
||||||
|
}}
|
||||||
|
}
|
||||||
108
runtime/src/obj/mod.rs
Normal file
108
runtime/src/obj/mod.rs
Normal file
@@ -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<ObjRef> {
|
||||||
|
self.attrs_mut()?.insert(sym, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// struct ObjRef
|
||||||
|
//
|
||||||
|
|
||||||
|
#[derive(Debug, Scan)]
|
||||||
|
pub struct ObjRef<T: Obj + ?Sized + Send + Sync = (dyn Obj + Send + Sync + 'static)> {
|
||||||
|
gc: Gc<RwLock<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Obj + ?Sized + Send + Sync> Clone for ObjRef<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
ObjRef { gc: self.gc.clone() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// impl ObjRef
|
||||||
|
//
|
||||||
|
|
||||||
|
impl<T: Obj + ?Sized + Send + Sync> ObjRef<T> {
|
||||||
|
/// Check object reference equality.
|
||||||
|
pub fn ref_eq(&self, other: &Self) -> bool {
|
||||||
|
let lhs: &RwLock<T> = &*self.gc.get();
|
||||||
|
let rhs: &RwLock<T> = &*other.get();
|
||||||
|
std::ptr::eq::<RwLock<T>>(lhs, rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Obj + Send + Sync + 'static> ObjRef<T> {
|
||||||
|
pub fn new(obj: T) -> Self {
|
||||||
|
ObjRef {
|
||||||
|
gc: Gc::new(RwLock::new(obj)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U> CoerceUnsized<ObjRef<U>> for ObjRef<T>
|
||||||
|
where
|
||||||
|
T: Obj + Send + Sync + ?Sized + Unsize<U>,
|
||||||
|
U: Obj + Send + Sync + ?Sized,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// impl Deref for ObjRef
|
||||||
|
//
|
||||||
|
|
||||||
|
impl<T: Obj + ?Sized + Send + Sync> Deref for ObjRef<T> {
|
||||||
|
type Target = Gc<RwLock<T>>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.gc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// impl DerefMut for ObjRef
|
||||||
|
//
|
||||||
|
|
||||||
|
impl<T: Obj + ?Sized + Send + Sync> DerefMut for ObjRef<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.gc
|
||||||
|
}
|
||||||
|
}
|
||||||
55
runtime/src/obj/names.rs
Normal file
55
runtime/src/obj/names.rs
Normal file
@@ -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<Sym> = 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__");
|
||||||
11
runtime/src/obj/str.rs
Normal file
11
runtime/src/obj/str.rs
Normal file
@@ -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);
|
||||||
|
|
||||||
95
runtime/src/obj/sym.rs
Normal file
95
runtime/src/obj/sym.rs
Normal file
@@ -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<Sym>) -> ObjRef<Sym> {
|
||||||
|
ObjRef::new(sym.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<usize> 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<ObjRef<Sym>> = Lazy::new(|| Sym::new_obj(*NIL_SYM));
|
||||||
|
|
||||||
|
pub static TRUE_OBJ: Lazy<ObjRef<Sym>> = Lazy::new(|| Sym::new_obj(*TRUE_SYM));
|
||||||
|
|
||||||
|
pub static FALSE_OBJ: Lazy<ObjRef<Sym>> = Lazy::new(|| Sym::new_obj(*FALSE_SYM));
|
||||||
|
|
||||||
|
pub static SYM_TY: Lazy<ObjRef<Ty>> = 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<ObjRef<Sym>> = Lazy::new(|| Sym::new_obj(*SYM_TY_SYM));
|
||||||
|
|
||||||
|
pub static SYM_ATTRS: Lazy<Attrs> = 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<Mutex<SymTable>> = Lazy::new(|| {
|
||||||
|
Mutex::new(SymTable::default())
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub(crate) fn global_name_lookup(sym: Sym) -> Option<Arc<String>> {
|
||||||
|
let table = SYMBOLS.lock()
|
||||||
|
.unwrap();
|
||||||
|
table.lookup(sym)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn global_sym_lookup(s: String) -> Option<Sym> {
|
||||||
|
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<String>;
|
||||||
|
pub type Locals = Interner<Sym>;
|
||||||
73
runtime/src/obj/test.rs
Normal file
73
runtime/src/obj/test.rs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
use crate::obj::{names::*, prelude::*};
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
use shredder::*;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
static TEST_LOCK: Lazy<Mutex<()>> = 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);
|
||||||
|
}
|
||||||
43
runtime/src/obj/ty.rs
Normal file
43
runtime/src/obj/ty.rs
Normal file
@@ -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<Self> {
|
||||||
|
// 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<ObjRef<Sym>> = Lazy::new(|| {
|
||||||
|
Sym::new_obj(*TY_TY_SYM)
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static TY_TY: Lazy<ObjRef<Ty>> = 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);
|
||||||
6
runtime/src/vm/frame.rs
Normal file
6
runtime/src/vm/frame.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
/// A stack call frame.
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
|
pub struct Frame {
|
||||||
|
last_pc: usize,
|
||||||
|
stack_base: usize,
|
||||||
|
}
|
||||||
41
runtime/src/vm/inst.rs
Normal file
41
runtime/src/vm/inst.rs
Normal file
@@ -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<Sym>),
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
}
|
||||||
51
runtime/src/vm/mod.rs
Normal file
51
runtime/src/vm/mod.rs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
mod frame;
|
||||||
|
pub mod inst;
|
||||||
|
|
||||||
|
use crate::obj::prelude::*;
|
||||||
|
|
||||||
|
use frame::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Vm {
|
||||||
|
stack: Vec<ObjRef>,
|
||||||
|
frames: Vec<Frame>,
|
||||||
|
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<ObjRef> {
|
||||||
|
&self.stack
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stack_mut(&mut self) -> &mut Vec<ObjRef> {
|
||||||
|
&mut self.stack
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, value: ObjRef) {
|
||||||
|
self.stack_mut().push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&mut self) -> Option<ObjRef> {
|
||||||
|
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,
|
||||||
|
}
|
||||||
5
src/bin/not.rs
Normal file
5
src/bin/not.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
use not_python::syn;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("hell world");
|
||||||
|
}
|
||||||
3
src/lib.rs
Normal file
3
src/lib.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pub mod syn;
|
||||||
|
|
||||||
|
pub use runtime;
|
||||||
162
src/syn/ast.rs
Normal file
162
src/syn/ast.rs
Normal file
@@ -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<BinExpr>),
|
||||||
|
Un(Box<UnExpr>),
|
||||||
|
Access(Box<AccessExpr>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Atom> for Expr {
|
||||||
|
fn from(other: Atom) -> Self {
|
||||||
|
Expr::Atom(other)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<BinExpr> for Expr {
|
||||||
|
fn from(other: BinExpr) -> Self {
|
||||||
|
Expr::Bin(other.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UnExpr> for Expr {
|
||||||
|
fn from(other: UnExpr) -> Self {
|
||||||
|
Expr::Un(other.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AccessExpr> 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<Access>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AccessExpr {
|
||||||
|
pub fn new_expr(expr: Expr, access: Vec<Access>) -> Expr {
|
||||||
|
Self { expr, access, }.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct Access {
|
||||||
|
pub access: String,
|
||||||
|
pub trailing: Vec<ExprTrail>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum ExprTrail {
|
||||||
|
Call(Vec<Expr>),
|
||||||
|
Index(Expr),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum Atom {
|
||||||
|
Ident(String),
|
||||||
|
Sym(String),
|
||||||
|
Num(i64),
|
||||||
|
String(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(text: &str) -> Result<Option<Vec<Stmt>>, Vec<lrpar::LexParseError<u32>>> {
|
||||||
|
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![])], },
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/syn/lexer.l
Normal file
27
src/syn/lexer.l
Normal file
@@ -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 ]+ ;
|
||||||
17
src/syn/mod.rs
Normal file
17
src/syn/mod.rs
Normal file
@@ -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;
|
||||||
144
src/syn/parser.y
Normal file
144
src/syn/parser.y
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
%start Body
|
||||||
|
|
||||||
|
%left '||'
|
||||||
|
%left '&&'
|
||||||
|
%left '<' '>' '<=' '>=' '==' '!='
|
||||||
|
%left '*' '/'
|
||||||
|
%left '+' '-'
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
Body -> Result<Vec<Stmt>>:
|
||||||
|
Body 'EOL' Stmt {
|
||||||
|
flatten($1, $3)
|
||||||
|
}
|
||||||
|
| Stmt { Ok(vec![$1?]) }
|
||||||
|
| { Ok(Vec::new()) }
|
||||||
|
;
|
||||||
|
|
||||||
|
Stmt -> Result<Stmt>:
|
||||||
|
Expr { Ok(Stmt::Expr($1?)) }
|
||||||
|
;
|
||||||
|
|
||||||
|
Expr -> Result<Expr>: BinExpr { $1 };
|
||||||
|
|
||||||
|
BinExpr -> Result<Expr>:
|
||||||
|
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<Expr>:
|
||||||
|
'+' UnExpr { Ok(UnExpr::new_expr(UnOp::Plus, $2?)) }
|
||||||
|
| '-' UnExpr { Ok(UnExpr::new_expr(UnOp::Minus, $2?)) }
|
||||||
|
| AccessExpr { $1 }
|
||||||
|
;
|
||||||
|
|
||||||
|
AccessExpr -> Result<Expr>:
|
||||||
|
AtomExpr AccessExprTail {
|
||||||
|
Ok(AccessExpr::new_expr($1?, $2?))
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
AccessExprTail -> Result<Vec<Access>>:
|
||||||
|
AccessExprTail '.' Ident ExprTrailing {
|
||||||
|
flatten($1, Ok(Access { access: $3?, trailing: $4? }))
|
||||||
|
}
|
||||||
|
| '.' Ident ExprTrailing {
|
||||||
|
Ok(vec![Access { access: $2?, trailing: $3? }])
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
ExprTrailing -> Result<Vec<ExprTrail>>:
|
||||||
|
ExprTrailing '(' FunArgs ')' { flatten($1, Ok(ExprTrail::Call($3?))) }
|
||||||
|
| ExprTrailing '[' Expr ']' { flatten($1, Ok(ExprTrail::Index($3?))) }
|
||||||
|
| { Ok(Vec::new()) }
|
||||||
|
;
|
||||||
|
|
||||||
|
FunArgs -> Result<Vec<Expr>>:
|
||||||
|
FunArgsTail { $1 }
|
||||||
|
| { Ok(Vec::new()) }
|
||||||
|
;
|
||||||
|
|
||||||
|
FunArgsTail -> Result<Vec<Expr>>:
|
||||||
|
FunArgsTail ',' Expr { flatten($1, $3) }
|
||||||
|
| Expr { Ok(vec![$1?]) }
|
||||||
|
;
|
||||||
|
|
||||||
|
AtomExpr -> Result<Expr>:
|
||||||
|
Atom { $1.map(Expr::Atom) }
|
||||||
|
| '(' Expr ')' { $2 }
|
||||||
|
;
|
||||||
|
|
||||||
|
Atom -> Result<Atom>:
|
||||||
|
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<String>:
|
||||||
|
'IDENT' {
|
||||||
|
let v = $1.map_err(|_| ())?;
|
||||||
|
let ident = $lexer.span_str(v.span()).to_string();
|
||||||
|
Ok(ident)
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
use crate::syn::ast::*;
|
||||||
|
|
||||||
|
type Result<T> = std::result::Result<T, ()>;
|
||||||
|
|
||||||
|
fn flatten<T>(head: Result<Vec<T>>, tail: Result<T>) -> Result<Vec<T>> {
|
||||||
|
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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user