use std::fmt; use std::hash::Hash; use internment::ArcIntern; pub use crate::syntax::tokens::ConstantType; use crate::syntax::Location; /// A structure represented a parsed program. /// /// One `Program` is associated with exactly one input file, and the /// vector is arranged in exactly the same order as the parsed file. /// Because this is the syntax layer, the program is guaranteed to be /// syntactically valid, but may be nonsense. There could be attempts /// to use unbound variables, for example, until after someone runs /// `validate` and it comes back without errors. #[derive(Clone, Debug, PartialEq)] pub struct Program { pub statements: Vec, } /// A Name. /// /// This is basically a string, but annotated with the place the string /// is in the source file. #[derive(Clone, Debug)] pub struct Name { pub name: String, pub location: Location, } impl Name { pub fn new(n: S, location: Location) -> Name { Name { name: n.to_string(), location, } } pub fn manufactured(n: S) -> Name { Name { name: n.to_string(), location: Location::manufactured(), } } pub fn intern(self) -> ArcIntern { ArcIntern::new(self.name) } } impl PartialEq for Name { fn eq(&self, other: &Self) -> bool { self.name == other.name } } impl Eq for Name {} impl Hash for Name { fn hash(&self, state: &mut H) { self.name.hash(state) } } impl fmt::Display for Name { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.name.fmt(f) } } /// A parsed statement. /// /// Statements are guaranteed to be syntactically valid, but may be /// complete nonsense at the semantic level. Which is to say, all the /// print statements were correctly formatted, and all the variables /// referenced are definitely valid symbols, but they may not have /// been defined or anything. /// /// Note that equivalence testing on statements is independent of /// source location; it is testing if the two statements say the same /// thing, not if they are the exact same statement. #[derive(Clone, Debug)] pub enum Statement { Binding(Location, Name, Expression), Print(Location, Name), } impl PartialEq for Statement { fn eq(&self, other: &Self) -> bool { match self { Statement::Binding(_, name1, expr1) => match other { Statement::Binding(_, name2, expr2) => name1 == name2 && expr1 == expr2, _ => false, }, Statement::Print(_, name1) => match other { Statement::Print(_, name2) => name1 == name2, _ => false, }, } } } /// An expression in the underlying syntax. /// /// Like statements, these expressions are guaranteed to have been /// formatted correctly, but may not actually make any sense. Also /// like Statements, the [`PartialEq`] implementation does not take /// source positions into account. #[derive(Clone, Debug)] pub enum Expression { Value(Location, Value), Reference(Location, String), Cast(Location, String, Box), Primitive(Location, String, Vec), } impl PartialEq for Expression { fn eq(&self, other: &Self) -> bool { match self { Expression::Value(_, val1) => match other { Expression::Value(_, val2) => val1 == val2, _ => false, }, Expression::Reference(_, var1) => match other { Expression::Reference(_, var2) => var1 == var2, _ => false, }, Expression::Cast(_, t1, e1) => match other { Expression::Cast(_, t2, e2) => t1 == t2 && e1 == e2, _ => false, }, Expression::Primitive(_, prim1, args1) => match other { Expression::Primitive(_, prim2, args2) => prim1 == prim2 && args1 == args2, _ => false, }, } } } /// A value from the source syntax #[derive(Clone, Debug, PartialEq, Eq)] pub enum Value { /// The value of the number, an optional base that it was written in, and any /// type information provided. /// /// u64 is chosen because it should be big enough to carry the amount of /// information we need, and technically we interpret -4 as the primitive unary /// operation "-" on the number 4. We'll translate this into a type-specific /// number at a later time. Number(Option, Option, u64), }