λ Support functions! #5
@@ -32,6 +32,7 @@ thiserror = "1.0.57"
|
||||
anyhow = "1.0.80"
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = { version = "0.3.18", features = ["time", "json", "env-filter"] }
|
||||
names = "0.14.0"
|
||||
|
||||
[build-dependencies]
|
||||
lalrpop = "0.20.2"
|
||||
|
||||
23
src/bin/gen_program.rs
Normal file
23
src/bin/gen_program.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use ngr::syntax::ProgramGenerator;
|
||||
use ngr::util::pretty::Allocator;
|
||||
use proptest::strategy::{Strategy, ValueTree};
|
||||
use proptest::test_runner::{Config, TestRunner};
|
||||
|
||||
fn main() -> Result<(), anyhow::Error> {
|
||||
let generator = ProgramGenerator::default();
|
||||
let runner_config = Config::default();
|
||||
let mut runner = TestRunner::new(runner_config);
|
||||
let program_tree = generator
|
||||
.new_tree(&mut runner)
|
||||
.map_err(|e| anyhow::anyhow!("Couldn't generate test program: {}", e))?;
|
||||
let program = program_tree.current();
|
||||
let allocator = Allocator::new();
|
||||
let mut stdout = std::io::stdout();
|
||||
|
||||
for top_level in program.into_iter() {
|
||||
let docbuilder = top_level.pretty(&allocator);
|
||||
docbuilder.render(78, &mut stdout)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -43,6 +43,7 @@ lalrpop_mod!(
|
||||
pub mod pretty;
|
||||
mod validate;
|
||||
|
||||
pub use crate::syntax::arbitrary::ProgramGenerator;
|
||||
pub use crate::syntax::ast::*;
|
||||
pub use crate::syntax::location::Location;
|
||||
pub use crate::syntax::name::Name;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -76,7 +76,7 @@ impl StructureDefinition {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum TopLevel {
|
||||
Expression(Expression),
|
||||
Structure(Location, Name, Vec<(Name, Type)>),
|
||||
Structure(Location, Name, Vec<(Name, Option<Type>)>),
|
||||
}
|
||||
|
||||
impl Located for TopLevel {
|
||||
@@ -205,6 +205,8 @@ pub enum Value {
|
||||
/// operation "-" on the number 4. We'll translate this into a type-specific
|
||||
/// number at a later time.
|
||||
Number(Option<u8>, Option<ConstantType>, u64),
|
||||
/// The empty value
|
||||
Void,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
|
||||
@@ -45,6 +45,8 @@ impl Expression {
|
||||
Some(ConstantType::U32) => Ok(Value::U32(*v as u32)),
|
||||
Some(ConstantType::U64) => Ok(Value::U64(*v)),
|
||||
},
|
||||
|
||||
super::Value::Void => Ok(Value::Void),
|
||||
},
|
||||
|
||||
Expression::Constructor(_, on, fields) => {
|
||||
|
||||
@@ -92,9 +92,12 @@ Structure: TopLevel = {
|
||||
}
|
||||
}
|
||||
|
||||
Field: (Name, Type) = {
|
||||
Field: (Name, Option<Type>) = {
|
||||
<s:@L> <name:"<var>"> <e:@L> ":" <field_type: Type> ";" =>
|
||||
(Name::new(name, Location::new(file_idx, s..e)), field_type)
|
||||
(Name::new(name, Location::new(file_idx, s..e)), Some(field_type)),
|
||||
<s:@L> <name:"<var>"> <e:@L> ";" =>
|
||||
(Name::new(name, Location::new(file_idx, s..e)), None),
|
||||
|
||||
}
|
||||
|
||||
Type: Type = {
|
||||
|
||||
@@ -84,7 +84,10 @@ impl Program {
|
||||
impl TopLevel {
|
||||
pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> {
|
||||
match self {
|
||||
TopLevel::Expression(expr) => expr.pretty(allocator),
|
||||
TopLevel::Expression(expr) => expr
|
||||
.pretty(allocator)
|
||||
.append(allocator.text(";"))
|
||||
.append(allocator.hardline()),
|
||||
TopLevel::Structure(_, name, fields) => allocator
|
||||
.text("struct")
|
||||
.append(allocator.space())
|
||||
@@ -95,17 +98,24 @@ impl TopLevel {
|
||||
.append(
|
||||
allocator
|
||||
.concat(fields.iter().map(|(name, ty)| {
|
||||
let type_bit = if let Some(ty) = ty {
|
||||
allocator
|
||||
.text(":")
|
||||
.append(allocator.space())
|
||||
.append(ty.pretty(allocator))
|
||||
} else {
|
||||
allocator.nil()
|
||||
};
|
||||
allocator
|
||||
.text(name.to_string())
|
||||
.append(allocator.text(":"))
|
||||
.append(allocator.space())
|
||||
.append(ty.pretty(allocator))
|
||||
.append(type_bit)
|
||||
.append(allocator.text(";"))
|
||||
.append(allocator.hardline())
|
||||
}))
|
||||
.nest(2),
|
||||
)
|
||||
.append(allocator.text("}")),
|
||||
.append(allocator.text("}"))
|
||||
.append(allocator.hardline()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -231,6 +241,8 @@ impl Value {
|
||||
|
||||
allocator.text(value_str)
|
||||
}
|
||||
|
||||
Value::Void => allocator.text("<void>"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use internment::ArcIntern;
|
||||
use logos::{Lexer, Logos};
|
||||
use std::fmt;
|
||||
use std::{fmt, str::FromStr};
|
||||
use thiserror::Error;
|
||||
|
||||
/// A single token of the input stream; used to help the parsing go down
|
||||
@@ -205,6 +205,43 @@ impl From<ConstantType> for cranelift_codegen::ir::Type {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StringNotConstantType();
|
||||
|
||||
impl FromStr for ConstantType {
|
||||
type Err = StringNotConstantType;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"i8" => Ok(ConstantType::I8),
|
||||
"i16" => Ok(ConstantType::I16),
|
||||
"i32" => Ok(ConstantType::I32),
|
||||
"i64" => Ok(ConstantType::I64),
|
||||
"u8" => Ok(ConstantType::U8),
|
||||
"u16" => Ok(ConstantType::U16),
|
||||
"u32" => Ok(ConstantType::U32),
|
||||
"u64" => Ok(ConstantType::U64),
|
||||
"void" => Ok(ConstantType::Void),
|
||||
_ => Err(StringNotConstantType()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ConstantType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ConstantType::I8 => write!(f, "i8"),
|
||||
ConstantType::I16 => write!(f, "i16"),
|
||||
ConstantType::I32 => write!(f, "i32"),
|
||||
ConstantType::I64 => write!(f, "i64"),
|
||||
ConstantType::U8 => write!(f, "u8"),
|
||||
ConstantType::U16 => write!(f, "u16"),
|
||||
ConstantType::U32 => write!(f, "u32"),
|
||||
ConstantType::U64 => write!(f, "u64"),
|
||||
ConstantType::Void => write!(f, "void"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstantType {
|
||||
/// Return the set of types that can be safely casted into this type.
|
||||
pub fn safe_casts_to(self) -> Vec<ConstantType> {
|
||||
@@ -268,6 +305,32 @@ impl ConstantType {
|
||||
ConstantType::U64 => "u64".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the set of all primitives that can return this
|
||||
/// type, along with the argument types for those primitives.
|
||||
///
|
||||
/// A "None" value as an argument type means that the argument
|
||||
/// type is unconstrained by the return type.
|
||||
pub fn primitives_for(&self) -> Vec<(crate::ir::Primitive, Vec<Option<ConstantType>>)> {
|
||||
use crate::ir::Primitive::*;
|
||||
|
||||
match self {
|
||||
ConstantType::Void => vec![(Print, vec![None])],
|
||||
ConstantType::I8 | ConstantType::I16 | ConstantType::I32 | ConstantType::I64 => vec![
|
||||
(Plus, vec![Some(*self), Some(*self)]),
|
||||
(Minus, vec![Some(*self), Some(*self)]),
|
||||
(Times, vec![Some(*self), Some(*self)]),
|
||||
(Divide, vec![Some(*self), Some(*self)]),
|
||||
(Negate, vec![Some(*self)]),
|
||||
],
|
||||
ConstantType::U8 | ConstantType::U16 | ConstantType::U32 | ConstantType::U64 => vec![
|
||||
(Plus, vec![Some(*self), Some(*self)]),
|
||||
(Minus, vec![Some(*self), Some(*self)]),
|
||||
(Times, vec![Some(*self), Some(*self)]),
|
||||
(Divide, vec![Some(*self), Some(*self)]),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, PartialEq)]
|
||||
|
||||
@@ -99,11 +99,8 @@ impl Program {
|
||||
}
|
||||
|
||||
TopLevel::Structure(loc, name, fields) => {
|
||||
let definition = StructureDefinition::new(
|
||||
loc,
|
||||
name.clone(),
|
||||
fields.into_iter().map(|(n, t)| (n, Some(t))).collect(),
|
||||
);
|
||||
let definition =
|
||||
StructureDefinition::new(loc, name.clone(), fields.into_iter().collect());
|
||||
|
||||
structures.insert(name, definition);
|
||||
}
|
||||
|
||||
@@ -124,6 +124,14 @@ impl InferenceEngine {
|
||||
// converting values is mostly tedious, because there's so many cases
|
||||
// involved
|
||||
syntax::Expression::Value(loc, val) => match val {
|
||||
syntax::Value::Void => (
|
||||
ir::Expression::Atomic(ir::ValueOrRef::Value(
|
||||
loc,
|
||||
ir::TypeOrVar::Primitive(PrimitiveType::Void),
|
||||
ir::Value::Void,
|
||||
)),
|
||||
ir::TypeOrVar::Primitive(PrimitiveType::Void),
|
||||
),
|
||||
syntax::Value::Number(base, mctype, value) => {
|
||||
let (newval, newtype) = match mctype {
|
||||
None => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod pretty;
|
||||
pub mod scoped_map;
|
||||
pub mod warning_result;
|
||||
pub mod weighted_map;
|
||||
|
||||
21
src/util/weighted_map.rs
Normal file
21
src/util/weighted_map.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
use rand::distributions::{Distribution, WeightedIndex};
|
||||
|
||||
pub struct WeightedMap<T: Clone> {
|
||||
index: WeightedIndex<usize>,
|
||||
items: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T: Clone> WeightedMap<T> {
|
||||
pub fn new(map: &[(usize, T)]) -> Self {
|
||||
let index = WeightedIndex::new(map.iter().map(|x| x.0)).unwrap();
|
||||
let items = map.iter().map(|x| x.1.clone()).collect();
|
||||
WeightedMap { index, items }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> Distribution<T> for WeightedMap<T> {
|
||||
fn sample<R: rand::prelude::Rng + ?Sized>(&self, rng: &mut R) -> T {
|
||||
let idx = self.index.sample(rng);
|
||||
self.items.get(idx).unwrap().clone()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user