From 5d0e34eb72d81fab2417b6f10caeda7a56b9f261 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sun, 7 May 2023 17:39:42 -0700 Subject: [PATCH] More fully document the IR. --- src/ir/ast.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 4abe3bd..8a7a4fd 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -13,6 +13,16 @@ use proptest::{ type Variable = ArcIntern; /// The representation of a program within our IR. For now, this is exactly one file. +/// +/// In addition, for the moment there's not really much of interest to hold here besides +/// the list of statements read from the file. Order is important. In the future, you +/// could imagine caching analysis information in this structure. +/// +/// `Program` implements both [`Pretty`] and [`Arbitrary`]. The former should be used +/// to print the structure whenever possible, especially if you value your or your +/// user's time. The latter is useful for testing that conversions of `Program` retain +/// their meaning. All `Program`s generated through [`Arbitrary`] are guaranteed to be +/// syntactically valid, although they may contain runtime issue like over- or underflow. #[derive(Debug)] pub struct Program { // For now, a program is just a vector of statements. In the future, we'll probably @@ -56,6 +66,12 @@ impl Arbitrary for Program { /// /// For now, this is either a binding site (`x = 4`) or a print statement /// (`print x`). Someday, though, more! +/// +/// As with `Program`, this type implements [`Pretty`], which should +/// be used to display the structure whenever possible. It does not +/// implement [`Arbitrary`], though, mostly because it's slightly +/// complicated to do so. +/// #[derive(Debug)] pub enum Statement { Binding(Location, Variable, Expression), @@ -84,6 +100,17 @@ where } /// The representation of an expression. +/// +/// Note that expressions, like everything else in this syntax tree, +/// supports [`Pretty`], and it's strongly encouraged that you use +/// that trait/module when printing these structures. +/// +/// Also, Expressions at this point in the compiler are explicitly +/// defined so that they are *not* recursive. By this point, if an +/// expression requires some other data (like, for example, invoking +/// a primitive), any subexpressions have been bound to variables so +/// that the referenced data will always either be a constant or a +/// variable reference. #[derive(Debug)] pub enum Expression { Value(Location, Value), @@ -120,6 +147,12 @@ where } } +/// A type representing the primitives allowed in the language. +/// +/// Having this as an enumeration avoids a lot of "this should not happen" +/// cases, but might prove to be cumbersome in the future. If that happens, +/// this may either become a more hierarchical enumeration, or we'll just +/// deal with the "this should not happen" cases. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Primitive { Plus, @@ -157,6 +190,11 @@ where } } +/// An expression that is always either a value or a reference. +/// +/// This is the type used to guarantee that we don't nest expressions +/// at this level. Instead, expressions that take arguments take one +/// of these, which can only be a constant or a reference. #[derive(Debug)] pub enum ValueOrRef { Value(Location, Value), @@ -185,8 +223,14 @@ impl From for Expression { } } +/// A constant in the IR. #[derive(Debug)] pub enum Value { + /// A numerical constant. + /// + /// The optional argument is the base that was used by the user to input + /// the number. By retaining it, we can ensure that if we need to print the + /// number back out, we can do so in the form that the user entered it. Number(Option, i64), }