CHECKPOINT: Initial syntax arbitrary implementation.

This commit is contained in:
2024-06-16 20:59:32 -07:00
parent cf7eff7a93
commit 212ca6cc53
13 changed files with 867 additions and 279 deletions

View File

@@ -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
View 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(())
}

View File

@@ -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

View File

@@ -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)]

View File

@@ -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) => {

View File

@@ -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 = {

View File

@@ -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(name.to_string())
.append(allocator.text(":"))
.text(":")
.append(allocator.space())
.append(ty.pretty(allocator))
} else {
allocator.nil()
};
allocator
.text(name.to_string())
.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>"),
}
}
}

View File

@@ -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)]

View File

@@ -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);
}

View File

@@ -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 => {

View File

@@ -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
View 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()
}
}