From 4c53419fc07abbeed18b0e426a1b3a92bf3aa88a Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sun, 24 Sep 2023 08:12:35 -0700 Subject: [PATCH 01/59] checkpoint --- src/syntax/parser.lalrpop | 42 +++++++++++++++++++++++++++++++++++++-- src/syntax/tokens.rs | 17 ++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index fc6a0c6..3c1d7f8 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -30,11 +30,15 @@ extern { enum Token { "=" => Token::Equals, ";" => Token::Semi, + "," => Token::Comma, "(" => Token::LeftParen, ")" => Token::RightParen, "<" => Token::LessThan, ">" => Token::GreaterThan, + "{" => Token::OpenBrace, + "}" => Token::CloseBrace, + "function" => Token::Function, "print" => Token::Print, "+" => Token::Operator('+'), @@ -53,11 +57,43 @@ extern { pub Program: Program = { // a program is just a set of statements - => Program { + => Program { statements: stmts } } +ProgramTopLevel: Vec = { + Function => unimplemented!(), + => { + rest.push(next); + rest + }, + => Vec::new(), +} + +Function: () = { + "function" "(" Arguments OptionalComma ")" Expression => unimplemented!(), +} + +Arguments: Vec = { + => { + args.push(arg); + args + }, + + => Vec::new(), +} + +Argument: Name = { + "> => + Name::new(v, Location::new(file_idx, name_start..name_end)), +} + +OptionalComma: () = { + => (), + "," => (), +} + Statements: Vec = { // a statement is either a set of statements followed by another // statement (note, here, that you can name the result of a sub-parse @@ -98,7 +134,7 @@ pub Statement: Statement = { e, ), - // Alternatively, a statement can just be a print statement. + // A statement can just be a print statement. "print" "> ";" => Statement::Print( Location::new(file_idx, ls..le), @@ -166,6 +202,8 @@ AtomicExpression: Expression = { "> => Expression::Reference(Location::new(file_idx, l..end), v.to_string()), // just a number "> => Expression::Value(Location::new(file_idx, l..end), Value::Number(n.0, n.1, n.2)), + // this expression could actually be a block! + "{" "}" => unimplemented!(), // finally, let people parenthesize expressions and get back to a // lower precedence "(" ")" => e, diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs index e521987..f6da79e 100644 --- a/src/syntax/tokens.rs +++ b/src/syntax/tokens.rs @@ -34,6 +34,9 @@ pub enum Token { #[token(";")] Semi, + #[token(",")] + Comma, + #[token("(")] LeftParen, @@ -46,6 +49,16 @@ pub enum Token { #[token(">")] GreaterThan, + #[token("{")] + OpenBrace, + + #[token("}")] + CloseBrace, + + #[token("lambda")] + #[token("function")] + Function, + // Next we take of any reserved words; I always like to put // these before we start recognizing more complicated regular // expressions. I don't think it matters, but it works for me. @@ -93,10 +106,14 @@ impl fmt::Display for Token { match self { Token::Equals => write!(f, "'='"), Token::Semi => write!(f, "';'"), + Token::Comma => write!(f, "','"), Token::LeftParen => write!(f, "'('"), Token::RightParen => write!(f, "')'"), Token::LessThan => write!(f, "<"), Token::GreaterThan => write!(f, ">"), + Token::OpenBrace => write!(f, "{{"), + Token::CloseBrace => write!(f, "}}"), + Token::Function => write!(f, "function"), Token::Print => write!(f, "'print'"), Token::Operator(c) => write!(f, "'{}'", c), Token::Number((None, otype, v)) => write!(f, "'{}{}'", v, display_optional_type(otype)), -- 2.53.0 From eba5227ebc642f1426371cda872bb9f5baa83916 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sun, 24 Sep 2023 17:25:55 -0700 Subject: [PATCH 02/59] reorder the format and build stages --- .github/workflows/rust.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 49785b2..bd293e1 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -25,9 +25,9 @@ jobs: toolchain: ${{ matrix.rust }} default: true override: true - - name: Build - run: cargo build - name: Format Check run: cargo fmt --check + - name: Build + run: cargo build - name: Run tests run: cargo test -- 2.53.0 From 736d27953f51207c6a0ec7821b53b68768540810 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 7 Oct 2023 11:06:28 +0200 Subject: [PATCH 03/59] Wire functions through everything, with some unimplemented, and add a basic scoped map. --- src/backend/into_crane.rs | 12 +++--- src/ir/ast.rs | 42 +++++++++++++++++++- src/ir/eval.rs | 10 +++-- src/ir/strings.rs | 13 +++++- src/lib.rs | 1 + src/repl.rs | 26 ++++++------ src/syntax.rs | 16 ++++---- src/syntax/arbitrary.rs | 34 ++++++++-------- src/syntax/ast.rs | 13 +++++- src/syntax/eval.rs | 15 ++++--- src/syntax/parser.lalrpop | 22 ++++++----- src/syntax/pretty.rs | 32 +++++++++++++-- src/syntax/validate.rs | 63 +++++++++++++++++++++++------ src/type_infer/ast.rs | 43 +++++++++++++++++++- src/type_infer/convert.rs | 48 ++++++++++++++++------ src/type_infer/finalize.rs | 17 ++++++-- src/util.rs | 1 + src/util/scoped_map.rs | 81 ++++++++++++++++++++++++++++++++++++++ 18 files changed, 392 insertions(+), 97 deletions(-) create mode 100644 src/util.rs create mode 100644 src/util/scoped_map.rs diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index e398e3d..4d3d7ea 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use crate::eval::PrimitiveType; -use crate::ir::{Expression, Primitive, Program, Statement, Type, Value, ValueOrRef}; +use crate::ir::{Expression, Primitive, Program, Statement, TopLevel, Type, Value, ValueOrRef}; use crate::syntax::ConstantType; use cranelift_codegen::entity::EntityRef; use cranelift_codegen::ir::{ @@ -120,12 +120,14 @@ impl Backend { // this is likely to become more cumbersome, and we'll want to separate // these off. But for now, given the amount of tables we keep around to track // state, it's easier to just include them. - for stmt in program.statements.drain(..) { - match stmt { + for item in program.items.drain(..) { + match item { + TopLevel::Function(_, _, _) => unimplemented!(), + // Print statements are fairly easy to compile: we just lookup the // output buffer, the address of the string to print, and the value // of whatever variable we're printing. Then we just call print. - Statement::Print(ann, t, var) => { + TopLevel::Statement(Statement::Print(ann, t, var)) => { // Get the output buffer (or null) from our general compilation context. let buffer_ptr = self.output_buffer_ptr(); let buffer_ptr = builder.ins().iconst(types::I64, buffer_ptr as i64); @@ -163,7 +165,7 @@ impl Backend { } // Variable binding is a little more con - Statement::Binding(_, var_name, _, value) => { + TopLevel::Statement(Statement::Binding(_, var_name, _, value)) => { // Kick off to the `Expression` implementation to see what value we're going // to bind to this variable. let (val, etype) = diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 478f393..24c408c 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -31,7 +31,7 @@ type Variable = ArcIntern; pub struct Program { // For now, a program is just a vector of statements. In the future, we'll probably // extend this to include a bunch of other information, but for now: just a list. - pub(crate) statements: Vec, + pub(crate) items: Vec, } impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Program @@ -42,7 +42,7 @@ where fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { let mut result = allocator.nil(); - for stmt in self.statements.iter() { + for stmt in self.items.iter() { // there's probably a better way to do this, rather than constantly // adding to the end, but this works. result = result @@ -69,6 +69,44 @@ impl Arbitrary for Program { } } +/// A thing that can sit at the top level of a file. +/// +/// For the moment, these are statements and functions. Other things +/// will likely be added in the future, but for now: just statements +/// and functions +#[derive(Debug)] +pub enum TopLevel { + Statement(Statement), + Function(Variable, Vec, Expression), +} + +impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b TopLevel +where + A: 'a, + D: ?Sized + DocAllocator<'a, A>, +{ + fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { + match self { + TopLevel::Function(name, args, body) => allocator + .text("function") + .append(allocator.space()) + .append(allocator.text(name.as_ref().to_string())) + .append( + allocator + .intersperse( + args.iter().map(|x| allocator.text(x.as_ref().to_string())), + ", ", + ) + .parens(), + ) + .append(allocator.space()) + .append(body.pretty(allocator)), + + TopLevel::Statement(stmt) => stmt.pretty(allocator), + } + } +} + /// The representation of a statement in the language. /// /// For now, this is either a binding site (`x = 4`) or a print statement diff --git a/src/ir/eval.rs b/src/ir/eval.rs index 6841508..46123f3 100644 --- a/src/ir/eval.rs +++ b/src/ir/eval.rs @@ -1,5 +1,5 @@ use crate::eval::{EvalEnvironment, EvalError, Value}; -use crate::ir::{Expression, Program, Statement}; +use crate::ir::{Expression, Program, Statement, TopLevel}; use super::{Primitive, Type, ValueOrRef}; @@ -12,14 +12,16 @@ impl Program { let mut env = EvalEnvironment::empty(); let mut stdout = String::new(); - for stmt in self.statements.iter() { + for stmt in self.items.iter() { match stmt { - Statement::Binding(_, name, _, value) => { + TopLevel::Function(_, _, _) => unimplemented!(), + + TopLevel::Statement(Statement::Binding(_, name, _, value)) => { let actual_value = value.eval(&env)?; env = env.extend(name.clone(), actual_value); } - Statement::Print(_, _, name) => { + TopLevel::Statement(Statement::Print(_, _, name)) => { let value = env.lookup(name.clone())?; let line = format!("{} = {}\n", name, value); stdout.push_str(&line); diff --git a/src/ir/strings.rs b/src/ir/strings.rs index 70f939f..6326672 100644 --- a/src/ir/strings.rs +++ b/src/ir/strings.rs @@ -1,4 +1,4 @@ -use super::ast::{Expression, Program, Statement}; +use super::ast::{Expression, Program, Statement, TopLevel}; use internment::ArcIntern; use std::collections::HashSet; @@ -10,7 +10,7 @@ impl Program { pub fn strings(&self) -> HashSet> { let mut result = HashSet::new(); - for stmt in self.statements.iter() { + for stmt in self.items.iter() { stmt.register_strings(&mut result); } @@ -18,6 +18,15 @@ impl Program { } } +impl TopLevel { + fn register_strings(&self, string_set: &mut HashSet>) { + match self { + TopLevel::Function(_, _, body) => body.register_strings(string_set), + TopLevel::Statement(stmt) => stmt.register_strings(string_set), + } + } +} + impl Statement { fn register_strings(&self, string_set: &mut HashSet>) { match self { diff --git a/src/lib.rs b/src/lib.rs index c155075..23f924c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,6 +68,7 @@ mod examples; pub mod ir; pub mod syntax; pub mod type_infer; +pub mod util; /// Implementation module for the high-level compiler. mod compiler; diff --git a/src/repl.rs b/src/repl.rs index fcf6b62..1600a0f 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,13 +1,13 @@ use crate::backend::{Backend, BackendError}; -use crate::syntax::{ConstantType, Location, ParserError, Statement}; +use crate::syntax::{ConstantType, Location, ParserError, Statement, TopLevel}; use crate::type_infer::TypeInferenceResult; +use crate::util::scoped_map::ScopedMap; use codespan_reporting::diagnostic::Diagnostic; use codespan_reporting::files::SimpleFiles; use codespan_reporting::term::{self, Config}; use cranelift_jit::JITModule; use cranelift_module::ModuleError; use pretty::termcolor::{ColorChoice, StandardStream}; -use std::collections::HashMap; /// A high-level REPL helper for NGR. /// @@ -23,7 +23,7 @@ use std::collections::HashMap; pub struct REPL { file_database: SimpleFiles, jitter: Backend, - variable_binding_sites: HashMap, + variable_binding_sites: ScopedMap, console: StandardStream, console_config: Config, } @@ -70,7 +70,7 @@ impl REPL { Ok(REPL { file_database: SimpleFiles::new(), jitter: Backend::jit(None)?, - variable_binding_sites: HashMap::new(), + variable_binding_sites: ScopedMap::new(), console, console_config, }) @@ -127,10 +127,14 @@ impl REPL { .get(entry) .expect("entry exists") .source(); - let syntax = Statement::parse(entry, source)?; + let syntax = TopLevel::parse(entry, source)?; let program = match syntax { - Statement::Binding(loc, name, expr) => { + TopLevel::Function(_, _, _) => { + unimplemented!() + } + + TopLevel::Statement(Statement::Binding(loc, name, expr)) => { // if this is a variable binding, and we've never defined this variable before, // we should tell cranelift about it. this is optimistic; if we fail to compile, // then we won't use this definition until someone tries again. @@ -141,15 +145,15 @@ impl REPL { } crate::syntax::Program { - statements: vec![ - Statement::Binding(loc.clone(), name.clone(), expr), - Statement::Print(loc, name), + items: vec![ + TopLevel::Statement(Statement::Binding(loc.clone(), name.clone(), expr)), + TopLevel::Statement(Statement::Print(loc, name)), ], } } - nonbinding => crate::syntax::Program { - statements: vec![nonbinding], + TopLevel::Statement(nonbinding) => crate::syntax::Program { + items: vec![TopLevel::Statement(nonbinding)], }, }; diff --git a/src/syntax.rs b/src/syntax.rs index 8d505a1..2a8acc8 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -8,7 +8,7 @@ //! //! * Turning the string into a series of language-specific [`Token`]s. //! * Taking those tokens, and computing a basic syntax tree from them, -//! using our parser ([`ProgramParser`] or [`StatementParser`], generated +//! using our parser ([`ProgramParser`] or [`TopLevelParser`], generated //! by [`lalrpop`](https://lalrpop.github.io/lalrpop/)). //! * Validating the tree we have parsed, using [`Program::validate`], //! returning any warnings or errors we have found. @@ -44,7 +44,7 @@ mod validate; use crate::syntax::arbitrary::GenerationEnvironment; pub use crate::syntax::ast::*; pub use crate::syntax::location::Location; -pub use crate::syntax::parser::{ProgramParser, StatementParser}; +pub use crate::syntax::parser::{ProgramParser, TopLevelParser}; pub use crate::syntax::tokens::{LexerError, Token}; #[cfg(test)] use ::pretty::{Arena, Pretty}; @@ -243,18 +243,18 @@ impl Program { } } -impl Statement { - /// Parse a statement that you have in memory, using the given index for [`Location`]s. +impl TopLevel { + /// Parse a top-level item that you have in memory, using the given index for [`Location`]s. /// /// As with [`Program::parse`], if you use a bad file index, you'll get weird behaviors /// when you try to print errors, but things should otherwise work fine. This function /// will only parse a single statement, which is useful in the REPL, but probably shouldn't /// be used when reading in whole files. - pub fn parse(file_idx: usize, buffer: &str) -> Result { + pub fn parse(file_idx: usize, buffer: &str) -> Result { let lexer = Token::lexer(buffer) .spanned() .map(|(token, range)| (range.start, token, range.end)); - StatementParser::new() + TopLevelParser::new() .parse(file_idx, lexer) .map_err(|e| ParserError::convert(file_idx, e)) } @@ -276,7 +276,7 @@ fn order_of_operations() { assert_eq!( Program::from_str(muladd1).unwrap(), Program { - statements: vec![Statement::Binding( + items: vec![TopLevel::Statement(Statement::Binding( Location::new(testfile, 0..1), Name::manufactured("x"), Expression::Primitive( @@ -303,7 +303,7 @@ fn order_of_operations() { ) ] ) - ),], + ))], } ); } diff --git a/src/syntax/arbitrary.rs b/src/syntax/arbitrary.rs index 93759e9..7ee27a1 100644 --- a/src/syntax/arbitrary.rs +++ b/src/syntax/arbitrary.rs @@ -1,4 +1,4 @@ -use crate::syntax::ast::{ConstantType, Expression, Name, Program, Statement, Value}; +use crate::syntax::ast::{ConstantType, Expression, Name, Program, Statement, TopLevel, Value}; use crate::syntax::location::Location; use proptest::sample::select; use proptest::{ @@ -57,38 +57,40 @@ impl Arbitrary for Program { fn arbitrary_with(genenv: Self::Parameters) -> Self::Strategy { proptest::collection::vec( - ProgramStatementInfo::arbitrary(), + ProgramTopLevelInfo::arbitrary(), genenv.block_length.clone(), ) - .prop_flat_map(move |mut items| { - let mut statements = Vec::new(); + .prop_flat_map(move |mut ptlis| { + let mut items = Vec::new(); let mut genenv = genenv.clone(); - for psi in items.drain(..) { + for psi in ptlis.drain(..) { if genenv.bindings.is_empty() || psi.should_be_binding { genenv.return_type = psi.binding_type; let expr = Expression::arbitrary_with(genenv.clone()); genenv.bindings.insert(psi.name.clone(), psi.binding_type); - statements.push( + items.push( expr.prop_map(move |expr| { - Statement::Binding(Location::manufactured(), psi.name.clone(), expr) + TopLevel::Statement(Statement::Binding( + Location::manufactured(), + psi.name.clone(), + expr, + )) }) .boxed(), ); } else { let printers = genenv.bindings.keys().map(|n| { - Just(Statement::Print( + Just(TopLevel::Statement(Statement::Print( Location::manufactured(), Name::manufactured(n), - )) + ))) }); - statements.push(Union::new(printers).boxed()); + items.push(Union::new(printers).boxed()); } } - statements - .prop_map(|statements| Program { statements }) - .boxed() + items.prop_map(|items| Program { items }).boxed() }) .boxed() } @@ -104,13 +106,13 @@ impl Arbitrary for Name { } #[derive(Debug)] -struct ProgramStatementInfo { +struct ProgramTopLevelInfo { should_be_binding: bool, name: Name, binding_type: ConstantType, } -impl Arbitrary for ProgramStatementInfo { +impl Arbitrary for ProgramTopLevelInfo { type Parameters = (); type Strategy = BoxedStrategy; @@ -121,7 +123,7 @@ impl Arbitrary for ProgramStatementInfo { ConstantType::arbitrary(), ) .prop_map( - |(should_be_binding, name, binding_type)| ProgramStatementInfo { + |(should_be_binding, name, binding_type)| ProgramTopLevelInfo { should_be_binding, name, binding_type, diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 6700a9f..4749acd 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -16,7 +16,18 @@ use crate::syntax::Location; /// `validate` and it comes back without errors. #[derive(Clone, Debug, PartialEq)] pub struct Program { - pub statements: Vec, + pub items: Vec, +} + +/// A thing that can sit at the top level of a file. +/// +/// For the moment, these are statements and functions. Other things +/// will likely be added in the future, but for now: just statements +/// and functions +#[derive(Clone, Debug, PartialEq)] +pub enum TopLevel { + Statement(Statement), + Function(Name, Vec, Expression), } /// A Name. diff --git a/src/syntax/eval.rs b/src/syntax/eval.rs index d6fda74..928d182 100644 --- a/src/syntax/eval.rs +++ b/src/syntax/eval.rs @@ -1,7 +1,7 @@ use internment::ArcIntern; use crate::eval::{EvalEnvironment, EvalError, PrimitiveType, Value}; -use crate::syntax::{ConstantType, Expression, Program, Statement}; +use crate::syntax::{ConstantType, Expression, Program, Statement, TopLevel}; use std::str::FromStr; impl Program { @@ -19,16 +19,19 @@ impl Program { let mut env = EvalEnvironment::empty(); let mut stdout = String::new(); - for stmt in self.statements.iter() { - // at this point, evaluation is pretty simple. just walk through each - // statement, in order, and record printouts as we come to them. + for stmt in self.items.iter() { match stmt { - Statement::Binding(_, name, value) => { + TopLevel::Function(_name, _arg_names, _body) => { + unimplemented!() + } + // at this point, evaluation is pretty simple. just walk through each + // statement, in order, and record printouts as we come to them. + TopLevel::Statement(Statement::Binding(_, name, value)) => { let actual_value = value.eval(&env)?; env = env.extend(name.clone().intern(), actual_value); } - Statement::Print(_, name) => { + TopLevel::Statement(Statement::Print(_, name)) => { let value = env.lookup(name.clone().intern())?; let line = format!("{} = {}\n", name, value); stdout.push_str(&line); diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index 3c1d7f8..2a0d882 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -9,7 +9,7 @@ //! eventually want to leave lalrpop behind.) //! use crate::syntax::{LexerError, Location}; -use crate::syntax::ast::{Program,Statement,Expression,Value,Name}; +use crate::syntax::ast::{Program,TopLevel,Statement,Expression,Value,Name}; use crate::syntax::tokens::{ConstantType, Token}; use internment::ArcIntern; @@ -57,21 +57,25 @@ extern { pub Program: Program = { // a program is just a set of statements - => Program { - statements: stmts + => Program { + items } } -ProgramTopLevel: Vec = { - Function => unimplemented!(), - => { - rest.push(next); +ProgramTopLevel: Vec = { + => { + rest.push(t); rest }, => Vec::new(), } -Function: () = { +pub TopLevel: TopLevel = { + => f, + => TopLevel::Statement(s), +} + +Function: TopLevel = { "function" "(" Arguments OptionalComma ")" Expression => unimplemented!(), } @@ -123,7 +127,7 @@ Statements: Vec = { } } -pub Statement: Statement = { +Statement: Statement = { // A statement can be a variable binding. Note, here, that we use this // funny @L thing to get the source location before the variable, so that // we can say that this statement spans across everything. diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index 6a9338f..b0b91cb 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -1,7 +1,7 @@ use crate::syntax::ast::{Expression, Program, Statement, Value}; use pretty::{DocAllocator, DocBuilder, Pretty}; -use super::ConstantType; +use super::{ConstantType, TopLevel}; impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Program where @@ -11,9 +11,9 @@ where fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { let mut result = allocator.nil(); - for stmt in self.statements.iter() { + for tl in self.items.iter() { result = result - .append(stmt.pretty(allocator)) + .append(tl.pretty(allocator)) .append(allocator.text(";")) .append(allocator.hardline()); } @@ -22,6 +22,32 @@ where } } +impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b TopLevel +where + A: 'a, + D: ?Sized + DocAllocator<'a, A>, +{ + fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { + match self { + TopLevel::Statement(stmt) => stmt.pretty(allocator), + TopLevel::Function(name, arg_names, body) => allocator + .text("function") + .append(allocator.space()) + .append(allocator.text(name.to_string())) + .append( + allocator + .intersperse( + arg_names.iter().map(|x| allocator.text(x.to_string())), + CommaSep {}, + ) + .parens(), + ) + .append(allocator.space()) + .append(body.pretty(allocator)), + } + } +} + impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Statement where A: 'a, diff --git a/src/syntax/validate.rs b/src/syntax/validate.rs index 30afe5c..58d25e8 100644 --- a/src/syntax/validate.rs +++ b/src/syntax/validate.rs @@ -1,9 +1,10 @@ use crate::{ eval::PrimitiveType, - syntax::{Expression, Location, Program, Statement}, + syntax::{Expression, Location, Program, Statement, TopLevel}, + util::scoped_map::ScopedMap, }; use codespan_reporting::diagnostic::Diagnostic; -use std::{collections::HashMap, str::FromStr}; +use std::str::FromStr; /// An error we found while validating the input program. /// @@ -65,7 +66,7 @@ impl Program { /// example, and generates warnings for things that are inadvisable but not /// actually a problem. pub fn validate(&self) -> (Vec, Vec) { - let mut bound_variables = HashMap::new(); + let mut bound_variables = ScopedMap::new(); self.validate_with_bindings(&mut bound_variables) } @@ -76,13 +77,13 @@ impl Program { /// actually a problem. pub fn validate_with_bindings( &self, - bound_variables: &mut HashMap, + bound_variables: &mut ScopedMap, ) -> (Vec, Vec) { let mut errors = vec![]; let mut warnings = vec![]; - for stmt in self.statements.iter() { - let (mut new_errors, mut new_warnings) = stmt.validate(bound_variables); + for stmt in self.items.iter() { + let (mut new_errors, mut new_warnings) = stmt.validate_with_bindings(bound_variables); errors.append(&mut new_errors); warnings.append(&mut new_warnings); } @@ -91,6 +92,44 @@ impl Program { } } +impl TopLevel { + /// Validate that the top level item makes semantic sense, not just syntactic + /// sense. + /// + /// This checks for things like references to variables that don't exist, for + /// example, and generates warnings for thins that are inadvisable but not + /// actually a problem. + pub fn validate(&self) -> (Vec, Vec) { + let mut bound_variables = ScopedMap::new(); + self.validate_with_bindings(&mut bound_variables) + } + + /// Validate that the top level item makes semantic sense, not just syntactic + /// sense. + /// + /// This checks for things like references to variables that don't exist, for + /// example, and generates warnings for thins that are inadvisable but not + /// actually a problem. + pub fn validate_with_bindings( + &self, + bound_variables: &mut ScopedMap, + ) -> (Vec, Vec) { + match self { + TopLevel::Function(name, arguments, body) => { + bound_variables.new_scope(); + bound_variables.insert(name.name.clone(), name.location.clone()); + for arg in arguments.iter() { + bound_variables.insert(arg.name.clone(), arg.location.clone()); + } + let result = body.validate(&bound_variables); + bound_variables.release_scope(); + result + } + TopLevel::Statement(stmt) => stmt.validate(bound_variables), + } + } +} + impl Statement { /// Validate that the statement makes semantic sense, not just syntactic sense. /// @@ -103,7 +142,7 @@ impl Statement { /// and warnings. fn validate( &self, - bound_variables: &mut HashMap, + bound_variables: &mut ScopedMap, ) -> (Vec, Vec) { let mut errors = vec![]; let mut warnings = vec![]; @@ -139,7 +178,7 @@ impl Statement { } impl Expression { - fn validate(&self, variable_map: &HashMap) -> (Vec, Vec) { + fn validate(&self, variable_map: &ScopedMap) -> (Vec, Vec) { match self { Expression::Value(_, _) => (vec![], vec![]), Expression::Reference(_, var) if variable_map.contains_key(var) => (vec![], vec![]), @@ -174,14 +213,14 @@ impl Expression { #[test] fn cast_checks_are_reasonable() { - let good_stmt = Statement::parse(0, "x = 4u8;").expect("valid test case"); - let (good_errs, good_warns) = good_stmt.validate(&mut HashMap::new()); + let good_stmt = TopLevel::parse(0, "x = 4u8;").expect("valid test case"); + let (good_errs, good_warns) = good_stmt.validate(); assert!(good_errs.is_empty()); assert!(good_warns.is_empty()); - let bad_stmt = Statement::parse(0, "x = 4u8;").expect("valid test case"); - let (bad_errs, bad_warns) = bad_stmt.validate(&mut HashMap::new()); + let bad_stmt = TopLevel::parse(0, "x = 4u8;").expect("valid test case"); + let (bad_errs, bad_warns) = bad_stmt.validate(); assert!(bad_warns.is_empty()); assert_eq!(bad_errs.len(), 1); diff --git a/src/type_infer/ast.rs b/src/type_infer/ast.rs index ff91a00..373690c 100644 --- a/src/type_infer/ast.rs +++ b/src/type_infer/ast.rs @@ -36,7 +36,7 @@ type Variable = ArcIntern; pub struct Program { // For now, a program is just a vector of statements. In the future, we'll probably // extend this to include a bunch of other information, but for now: just a list. - pub(crate) statements: Vec, + pub(crate) items: Vec, } impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Program @@ -47,7 +47,7 @@ where fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { let mut result = allocator.nil(); - for stmt in self.statements.iter() { + for stmt in self.items.iter() { // there's probably a better way to do this, rather than constantly // adding to the end, but this works. result = result @@ -60,6 +60,45 @@ where } } +/// A thing that can sit at the top level of a file. +/// +/// For the moment, these are statements and functions. Other things +/// will likely be added in the future, but for now: just statements +/// and functions +#[derive(Debug)] +pub enum TopLevel { + Statement(Statement), + Function(Variable, Vec, Expression), +} + +impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b TopLevel +where + A: 'a, + D: ?Sized + DocAllocator<'a, A>, +{ + fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { + match self { + TopLevel::Function(name, args, body) => allocator + .text("function") + .append(allocator.space()) + .append(allocator.text(name.as_ref().to_string())) + .append(allocator.space()) + .append( + allocator + .intersperse( + args.iter().map(|x| allocator.text(x.as_ref().to_string())), + ", ", + ) + .parens(), + ) + .append(allocator.space()) + .append(body.pretty(allocator)), + + TopLevel::Statement(stmt) => stmt.pretty(allocator), + } + } +} + /// The representation of a statement in the language. /// /// For now, this is either a binding site (`x = 4`) or a print statement diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index e53cdb6..56c5732 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -18,20 +18,42 @@ pub fn convert_program( mut program: syntax::Program, constraint_db: &mut Vec, ) -> ir::Program { - let mut statements = Vec::new(); + let mut items = Vec::new(); let mut renames = HashMap::new(); let mut bindings = HashMap::new(); - for stmt in program.statements.drain(..) { - statements.append(&mut convert_statement( - stmt, + for item in program.items.drain(..) { + items.append(&mut convert_top_level( + item, constraint_db, &mut renames, &mut bindings, )); } - ir::Program { statements } + ir::Program { items } +} + +/// This function takes a top-level item and converts it into the IR version of the +/// program, with all the appropriate type variables introduced and their constraints +/// added to the given database. +pub fn convert_top_level( + top_level: syntax::TopLevel, + constraint_db: &mut Vec, + renames: &mut HashMap, ArcIntern>, + bindings: &mut HashMap, Type>, +) -> Vec { + match top_level { + syntax::TopLevel::Function(_, _arg_name, _) => { + unimplemented!() + } + syntax::TopLevel::Statement(stmt) => { + convert_statement(stmt, constraint_db, renames, bindings) + .drain(..) + .map(ir::TopLevel::Statement) + .collect() + } + } } /// This function takes a syntactic statements and converts it into a series of @@ -269,11 +291,11 @@ mod tests { (expr, stmts, constraints, ty) } - fn infer_statement(x: syntax::Statement) -> (Vec, Vec) { + fn infer_top_level(x: syntax::TopLevel) -> (Vec, Vec) { let mut constraints = Vec::new(); let mut renames = HashMap::new(); let mut bindings = HashMap::new(); - let res = convert_statement(x, &mut constraints, &mut renames, &mut bindings); + let res = convert_top_level(x, &mut constraints, &mut renames, &mut bindings); (res, constraints) } @@ -321,24 +343,24 @@ mod tests { #[test] fn one_plus_one_plus_one() { - let stmt = syntax::Statement::parse(1, "x = 1 + 1 + 1;").expect("basic parse"); - let (stmts, constraints) = infer_statement(stmt); + let stmt = syntax::TopLevel::parse(1, "x = 1 + 1 + 1;").expect("basic parse"); + let (stmts, constraints) = infer_top_level(stmt); assert_eq!(stmts.len(), 2); - let ir::Statement::Binding( + let ir::TopLevel::Statement(ir::Statement::Binding( _args, name1, temp_ty1, ir::Expression::Primitive(_, primty1, ir::Primitive::Plus, primargs1), - ) = stmts.get(0).expect("item two") + )) = stmts.get(0).expect("item two") else { panic!("Failed to match first statement"); }; - let ir::Statement::Binding( + let ir::TopLevel::Statement(ir::Statement::Binding( _args, name2, temp_ty2, ir::Expression::Primitive(_, primty2, ir::Primitive::Plus, primargs2), - ) = stmts.get(1).expect("item two") + )) = stmts.get(1).expect("item two") else { panic!("Failed to match second statement"); }; diff --git a/src/type_infer/finalize.rs b/src/type_infer/finalize.rs index 9c70944..7f8f5d6 100644 --- a/src/type_infer/finalize.rs +++ b/src/type_infer/finalize.rs @@ -6,14 +6,25 @@ pub fn finalize_program( resolutions: &TypeResolutions, ) -> output::Program { output::Program { - statements: program - .statements + items: program + .items .drain(..) - .map(|x| finalize_statement(x, resolutions)) + .map(|x| finalize_top_level(x, resolutions)) .collect(), } } +fn finalize_top_level(item: input::TopLevel, resolutions: &TypeResolutions) -> output::TopLevel { + match item { + input::TopLevel::Function(name, args, body) => { + output::TopLevel::Function(name, args, finalize_expression(body, resolutions)) + } + input::TopLevel::Statement(stmt) => { + output::TopLevel::Statement(finalize_statement(stmt, resolutions)) + } + } +} + fn finalize_statement( statement: input::Statement, resolutions: &TypeResolutions, diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..b7271cc --- /dev/null +++ b/src/util.rs @@ -0,0 +1 @@ +pub mod scoped_map; diff --git a/src/util/scoped_map.rs b/src/util/scoped_map.rs new file mode 100644 index 0000000..dc8a9a1 --- /dev/null +++ b/src/util/scoped_map.rs @@ -0,0 +1,81 @@ +use std::{borrow::Borrow, collections::HashMap, hash::Hash}; + +/// A version of [`std::collections::HashMap`] with a built-in notion of scope. +pub struct ScopedMap { + scopes: Vec>, +} + +impl ScopedMap { + /// Generate a new scoped map. + /// + /// In addition to generate the map structure, this method also generates + /// an initial scope for use by the caller. + pub fn new() -> ScopedMap { + ScopedMap { + scopes: vec![HashMap::new()], + } + } + + /// Get a value from the scoped map. + pub fn get(&self, k: &Q) -> Option<&V> + where + K: Borrow, + Q: Hash + Eq + ?Sized, + { + for map in self.scopes.iter().rev() { + match map.get(k) { + None => continue, + Some(v) => return Some(v), + } + } + None + } + + /// Returns true if the map contains the given key. + pub fn contains_key(&self, k: &K) -> bool { + self.scopes.iter().any(|x| x.contains_key(k)) + } + + /// Insert a value into the current binding scope. + /// + /// If this variable is bound in the current scope, then its value will be + /// overridden. If it's bound in a previous scope, however, that value will + /// be shadowed, so that its value will preserved if/when the current scope + /// is popped. + pub fn insert(&mut self, k: K, v: V) { + self.scopes + .last_mut() + .expect("tried to insert into ScopedMap with no scopes") + .insert(k, v); + } + + /// Create a new scope. + /// + /// Modifications to this scope will shadow all previous scopes without + /// modifying them. Consider the following examples: + /// + /// ``` + /// use ngr::util::scoped_map::ScopedMap; + /// + /// let mut example1 = ScopedMap::new(); + /// example1.insert(1, true); + /// example1.insert(1, false); + /// assert_eq!(Some(&false), example1.get(&1)); + /// let mut example2 = ScopedMap::new(); + /// example2.insert(1, true); + /// example2.new_scope(); + /// example2.insert(1, false); + /// assert_eq!(Some(&false), example2.get(&1)); + /// example2.release_scope().expect("scope releases"); + /// assert_eq!(Some(&true), example2.get(&1)); + /// ``` + pub fn new_scope(&mut self) { + self.scopes.push(HashMap::new()); + } + + /// Pop the current scope, returning to whatever was bound in the previous + /// scope. If there is no prior scope, `None` will be returned. + pub fn release_scope(&mut self) -> Option> { + self.scopes.pop() + } +} -- 2.53.0 From ca26d0ea40b2b70fc81cca1becfe8d1d5d778fe6 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 7 Oct 2023 11:25:49 +0200 Subject: [PATCH 04/59] Fix the parser, make the function name optional. --- src/syntax/ast.rs | 2 +- src/syntax/parser.lalrpop | 9 ++++++++- src/syntax/pretty.rs | 6 +++++- src/syntax/validate.rs | 4 +++- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 4749acd..b97851f 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -27,7 +27,7 @@ pub struct Program { #[derive(Clone, Debug, PartialEq)] pub enum TopLevel { Statement(Statement), - Function(Name, Vec, Expression), + Function(Option, Vec, Expression), } /// A Name. diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index 2a0d882..d433571 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -76,7 +76,14 @@ pub TopLevel: TopLevel = { } Function: TopLevel = { - "function" "(" Arguments OptionalComma ")" Expression => unimplemented!(), + "function" "(" OptionalComma ")" => + TopLevel::Function(opt_name, args, exp), +} + +OptionalName: Option = { + "> => + Some(Name::new(v, Location::new(file_idx, name_start..name_end))), + => None, } Arguments: Vec = { diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index b0b91cb..c52a827 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -33,7 +33,11 @@ where TopLevel::Function(name, arg_names, body) => allocator .text("function") .append(allocator.space()) - .append(allocator.text(name.to_string())) + .append( + name.as_ref() + .map(|x| allocator.text(x.to_string())) + .unwrap_or_else(|| allocator.nil()), + ) .append( allocator .intersperse( diff --git a/src/syntax/validate.rs b/src/syntax/validate.rs index 58d25e8..f175eed 100644 --- a/src/syntax/validate.rs +++ b/src/syntax/validate.rs @@ -117,7 +117,9 @@ impl TopLevel { match self { TopLevel::Function(name, arguments, body) => { bound_variables.new_scope(); - bound_variables.insert(name.name.clone(), name.location.clone()); + if let Some(name) = name { + bound_variables.insert(name.name.clone(), name.location.clone()); + } for arg in arguments.iter() { bound_variables.insert(arg.name.clone(), arg.location.clone()); } -- 2.53.0 From 87d027bf8d6cda1f0981231de9d6fae8bb84c3b7 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 7 Oct 2023 14:36:17 +0200 Subject: [PATCH 05/59] Checkpoint: pushed through type checking, at least. --- src/ir/ast.rs | 5 ++- src/type_infer/ast.rs | 76 +++++++++++++++++++++++++++++++------- src/type_infer/convert.rs | 60 +++++++++++++++++++++++++----- src/type_infer/finalize.rs | 20 ++++++++-- 4 files changed, 132 insertions(+), 29 deletions(-) diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 24c408c..6a7b62d 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -77,7 +77,7 @@ impl Arbitrary for Program { #[derive(Debug)] pub enum TopLevel { Statement(Statement), - Function(Variable, Vec, Expression), + Function(Variable, Vec, Vec, Expression), } impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b TopLevel @@ -87,7 +87,7 @@ where { fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { match self { - TopLevel::Function(name, args, body) => allocator + TopLevel::Function(name, args, stmts, body) => allocator .text("function") .append(allocator.space()) .append(allocator.text(name.as_ref().to_string())) @@ -378,6 +378,7 @@ where #[derive(Clone, Debug, Eq, PartialEq)] pub enum Type { Primitive(PrimitiveType), + Function(Vec, Box), } impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Type diff --git a/src/type_infer/ast.rs b/src/type_infer/ast.rs index 373690c..daffbf7 100644 --- a/src/type_infer/ast.rs +++ b/src/type_infer/ast.rs @@ -68,7 +68,7 @@ where #[derive(Debug)] pub enum TopLevel { Statement(Statement), - Function(Variable, Vec, Expression), + Function(Variable, Vec, Vec, Expression), } impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b TopLevel @@ -78,22 +78,36 @@ where { fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { match self { - TopLevel::Function(name, args, body) => allocator - .text("function") - .append(allocator.space()) - .append(allocator.text(name.as_ref().to_string())) - .append(allocator.space()) - .append( - allocator - .intersperse( - args.iter().map(|x| allocator.text(x.as_ref().to_string())), - ", ", + TopLevel::Function(name, args, stmts, expr) => { + let base = allocator + .text("function") + .append(allocator.space()) + .append(allocator.text(name.as_ref().to_string())) + .append(allocator.space()) + .append( + pretty_comma_separated( + allocator, + &args + .iter() + .map(|x| allocator.text(x.as_ref().to_string())) + .collect(), ) .parens(), - ) - .append(allocator.space()) - .append(body.pretty(allocator)), + ) + .append(allocator.space()); + let mut body = allocator.nil(); + for stmt in stmts { + body = body + .append(stmt.pretty(allocator)) + .append(allocator.text(";")) + .append(allocator.hardline()); + } + body = body.append(expr.pretty(allocator)); + body = body.append(allocator.hardline()); + body = body.braces(); + base.append(body) + } TopLevel::Statement(stmt) => stmt.pretty(allocator), } } @@ -310,6 +324,7 @@ where pub enum Type { Variable(Location, ArcIntern), Primitive(PrimitiveType), + Function(Vec, Box), } impl Type { @@ -327,6 +342,14 @@ where match self { Type::Variable(_, x) => allocator.text(x.to_string()), Type::Primitive(pt) => allocator.text(format!("{}", pt)), + Type::Function(args, rettype) => { + pretty_comma_separated(allocator, &args.iter().collect()) + .parens() + .append(allocator.space()) + .append(allocator.text("->")) + .append(allocator.space()) + .append(rettype.pretty(allocator)) + } } } } @@ -336,6 +359,18 @@ impl fmt::Display for Type { match self { Type::Variable(_, x) => write!(f, "{}", x), Type::Primitive(pt) => pt.fmt(f), + Type::Function(args, ret) => { + write!(f, "(")?; + let mut argiter = args.iter().peekable(); + while let Some(arg) = argiter.next() { + arg.fmt(f)?; + if argiter.peek().is_some() { + write!(f, ",")?; + } + } + write!(f, "->")?; + ret.fmt(f) + } } } } @@ -373,3 +408,16 @@ pub fn gentype() -> Type { Type::Variable(Location::manufactured(), name) } + +fn pretty_comma_separated<'a, D, A, P>( + allocator: &'a D, + args: &Vec

, +) -> pretty::DocBuilder<'a, D, A> +where + A: 'a, + D: ?Sized + DocAllocator<'a, A>, + P: Pretty<'a, D, A>, +{ + let individuals = args.iter().map(|x| x.pretty(allocator)); + allocator.intersperse(individuals, ", ") +} diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 56c5732..bff8c2a 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -44,8 +44,41 @@ pub fn convert_top_level( bindings: &mut HashMap, Type>, ) -> Vec { match top_level { - syntax::TopLevel::Function(_, _arg_name, _) => { - unimplemented!() + syntax::TopLevel::Function(name, args, expr) => { + // First, let us figure out what we're going to name this function. If the user + // didn't provide one, we'll just call it "function:" for them. (We'll + // want a name for this function, eventually, so we might as well do it now.) + // + // If they did provide a name, see if we're shadowed. IF we are, then we'll have + // to specialize the name a bit. Otherwise we'll stick with their name. + let funname = match name { + None => ir::gensym("function"), + Some(unbound) => finalize_name(bindings, renames, unbound), + }; + + // Now we manufacture types for the inputs and outputs, and then a type for the + // function itself. We're not going to make any claims on these types, yet; they're + // all just unknown type variables we need to work out. + let argtypes: Vec = args.iter().map(|_| ir::gentype()).collect(); + let rettype = ir::gentype(); + let funtype = Type::Function(argtypes.clone(), Box::new(rettype.clone())); + + // Now let's bind these types into the environment. First, we bind our function + // namae to the function type we just generated. + bindings.insert(funname, funtype); + // And then we attach the argument names to the argument types. (We have to go + // convert all the names, first.) + let iargs: Vec> = + args.iter().map(|x| ArcIntern::new(x.to_string())).collect(); + assert_eq!(argtypes.len(), iargs.len()); + for (arg_name, arg_type) in iargs.iter().zip(argtypes) { + bindings.insert(arg_name.clone(), arg_type.clone()); + } + + let (stmts, expr, ty) = convert_expression(expr, constraint_db, renames, bindings); + constraint_db.push(Constraint::Equivalent(expr.location().clone(), rettype, ty)); + + vec![ir::TopLevel::Function(funname, iargs, stmts, expr)] } syntax::TopLevel::Statement(stmt) => { convert_statement(stmt, constraint_db, renames, bindings) @@ -93,14 +126,7 @@ fn convert_statement( syntax::Statement::Binding(loc, name, expr) => { let (mut prereqs, expr, ty) = convert_expression(expr, constraint_db, renames, bindings); - let iname = ArcIntern::new(name.to_string()); - let final_name = if bindings.contains_key(&iname) { - let new_name = ir::gensym(iname.as_str()); - renames.insert(iname, new_name.clone()); - new_name - } else { - iname - }; + let final_name = finalize_name(bindings, renames, name); bindings.insert(final_name.clone(), ty.clone()); prereqs.push(ir::Statement::Binding(loc, final_name, ty, expr)); @@ -260,6 +286,20 @@ fn simplify_expr(expr: ir::Expression, stmts: &mut Vec) -> ir::Va } } +fn finalize_name( + bindings: &HashMap, Type>, + renames: &mut HashMap, ArcIntern>, + name: syntax::Name, +) -> ArcIntern { + if bindings.contains_key(&ArcIntern::new(name.name)) { + let new_name = ir::gensym(&name.name); + renames.insert(ArcIntern::new(name.name.to_string()), new_name.clone()); + new_name + } else { + ArcIntern::new(name.to_string()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/type_infer/finalize.rs b/src/type_infer/finalize.rs index 7f8f5d6..763574c 100644 --- a/src/type_infer/finalize.rs +++ b/src/type_infer/finalize.rs @@ -16,9 +16,14 @@ pub fn finalize_program( fn finalize_top_level(item: input::TopLevel, resolutions: &TypeResolutions) -> output::TopLevel { match item { - input::TopLevel::Function(name, args, body) => { - output::TopLevel::Function(name, args, finalize_expression(body, resolutions)) - } + input::TopLevel::Function(name, args, mut body, expr) => output::TopLevel::Function( + name, + args, + body.drain(..) + .map(|x| finalize_statement(x, resolutions)) + .collect(), + finalize_expression(expr, resolutions), + ), input::TopLevel::Statement(stmt) => { output::TopLevel::Statement(finalize_statement(stmt, resolutions)) } @@ -73,6 +78,12 @@ fn finalize_type(ty: input::Type, resolutions: &TypeResolutions) -> output::Type None => panic!("Did not resolve type for type variable {}", tvar), Some(pt) => output::Type::Primitive(*pt), }, + input::Type::Function(mut args, ret) => output::Type::Function( + args.drain(..) + .map(|x| finalize_type(x, resolutions)) + .collect(), + Box::new(finalize_type(*ret, resolutions)), + ), } } @@ -89,6 +100,9 @@ fn finalize_val_or_ref( match val { input::Value::Unknown(base, value) => match new_type { + output::Type::Function(_, _) => { + panic!("Somehow inferred that a constant was a function") + } output::Type::Primitive(PrimitiveType::U8) => output::ValueOrRef::Value( loc, new_type, -- 2.53.0 From 71228b9e0915fd7ee70da2c3656305aba9da1775 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 14 Oct 2023 16:39:16 -0700 Subject: [PATCH 06/59] Checkpoint --- src/backend/into_crane.rs | 2 +- src/eval.rs | 18 ++++- src/eval/primop.rs | 15 +++- src/eval/primtype.rs | 43 +++++++---- src/eval/value.rs | 86 ++++++++++++++++++--- src/ir/ast.rs | 58 +++++++++++--- src/ir/eval.rs | 6 +- src/ir/strings.rs | 7 +- src/syntax.rs | 2 +- src/syntax/location.rs | 4 +- src/syntax/validate.rs | 2 +- src/type_infer.rs | 2 +- src/type_infer/ast.rs | 19 +---- src/type_infer/convert.rs | 4 +- src/type_infer/solve.rs | 158 ++++++++++++++++++++++++++++++++++++-- src/util.rs | 1 + src/util/pretty.rs | 47 ++++++++++++ src/util/scoped_map.rs | 6 ++ 18 files changed, 402 insertions(+), 78 deletions(-) create mode 100644 src/util/pretty.rs diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index 4d3d7ea..a9989de 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -122,7 +122,7 @@ impl Backend { // state, it's easier to just include them. for item in program.items.drain(..) { match item { - TopLevel::Function(_, _, _) => unimplemented!(), + TopLevel::Function(_, _, _, _) => unimplemented!(), // Print statements are fairly easy to compile: we just lookup the // output buffer, the address of the string to print, and the value diff --git a/src/eval.rs b/src/eval.rs index a593a63..b45b4d6 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -46,6 +46,8 @@ pub use value::Value; use crate::backend::BackendError; +use self::primtype::UnknownPrimType; + /// All of the errors that can happen trying to evaluate an NGR program. /// /// This is yet another standard [`thiserror::Error`] type, but with the @@ -71,9 +73,13 @@ pub enum EvalError { ExitCode(std::process::ExitStatus), #[error("Unexpected output at runtime: {0}")] RuntimeOutput(String), + #[error("Cannot cast to function type: {0}")] + CastToFunction(String), + #[error(transparent)] + UnknownPrimType(#[from] UnknownPrimType), } -impl PartialEq for EvalError { +impl PartialEq for EvalError { fn eq(&self, other: &Self) -> bool { match self { EvalError::Lookup(a) => match other { @@ -115,6 +121,16 @@ impl PartialEq for EvalError { EvalError::RuntimeOutput(b) => a == b, _ => false, }, + + EvalError::CastToFunction(a) => match other { + EvalError::CastToFunction(b) => a == b, + _ => false, + }, + + EvalError::UnknownPrimType(a) => match other { + EvalError::UnknownPrimType(b) => a == b, + _ => false, + }, } } } diff --git a/src/eval/primop.rs b/src/eval/primop.rs index 01a8dd1..41634a3 100644 --- a/src/eval/primop.rs +++ b/src/eval/primop.rs @@ -1,6 +1,8 @@ use crate::eval::primtype::PrimitiveType; use crate::eval::value::Value; +use super::primtype::{UnknownPrimType, ValuePrimitiveTypeError}; + /// Errors that can occur running primitive operations in the evaluators. #[derive(Clone, Debug, PartialEq, thiserror::Error)] pub enum PrimOpError { @@ -14,7 +16,7 @@ pub enum PrimOpError { /// This variant covers when an operator must take a particular /// type, but the user has provided a different one. #[error("Bad type for operator {0}: {1}")] - BadTypeFor(&'static str, Value), + BadTypeFor(String, Value), /// Probably obvious from the name, but just to be very clear: this /// happens when you pass three arguments to a two argument operator, /// etc. Technically that's a type error of some sort, but we split @@ -28,8 +30,10 @@ pub enum PrimOpError { from: PrimitiveType, to: PrimitiveType, }, - #[error("Unknown primitive type {0}")] - UnknownPrimType(String), + #[error(transparent)] + UnknownPrimType(#[from] UnknownPrimType), + #[error(transparent)] + ValuePrimitiveTypeError(#[from] ValuePrimitiveTypeError), } // Implementing primitives in an interpreter like this is *super* tedious, @@ -63,7 +67,7 @@ impl Value { Value::I16(x) => Ok(Value::I16(x.wrapping_neg())), Value::I32(x) => Ok(Value::I32(x.wrapping_neg())), Value::I64(x) => Ok(Value::I64(x.wrapping_neg())), - _ => Err(PrimOpError::BadTypeFor("-", value.clone())), + _ => Err(PrimOpError::BadTypeFor("-".to_string(), value.clone())), }, _ => Err(PrimOpError::BadArgCount(operation.to_owned(), 1)), } @@ -135,6 +139,9 @@ impl Value { right.clone(), )), }, + Value::Function(_, _) => { + Err(PrimOpError::BadTypeFor(operation.to_string(), left.clone())) + } } } diff --git a/src/eval/primtype.rs b/src/eval/primtype.rs index 7690137..de1e6eb 100644 --- a/src/eval/primtype.rs +++ b/src/eval/primtype.rs @@ -31,17 +31,28 @@ impl Display for PrimitiveType { } } -impl<'a> From<&'a Value> for PrimitiveType { - fn from(value: &Value) -> Self { +#[derive(Clone, Debug, PartialEq, thiserror::Error)] +pub enum ValuePrimitiveTypeError { + #[error("Could not convert function value to primitive type (possible function name: {0:?}")] + CannotConvertFunction(Option), +} + +impl<'a> TryFrom<&'a Value> for PrimitiveType { + type Error = ValuePrimitiveTypeError; + + fn try_from(value: &'a Value) -> Result { match value { - Value::I8(_) => PrimitiveType::I8, - Value::I16(_) => PrimitiveType::I16, - Value::I32(_) => PrimitiveType::I32, - Value::I64(_) => PrimitiveType::I64, - Value::U8(_) => PrimitiveType::U8, - Value::U16(_) => PrimitiveType::U16, - Value::U32(_) => PrimitiveType::U32, - Value::U64(_) => PrimitiveType::U64, + Value::I8(_) => Ok(PrimitiveType::I8), + Value::I16(_) => Ok(PrimitiveType::I16), + Value::I32(_) => Ok(PrimitiveType::I32), + Value::I64(_) => Ok(PrimitiveType::I64), + Value::U8(_) => Ok(PrimitiveType::U8), + Value::U16(_) => Ok(PrimitiveType::U16), + Value::U32(_) => Ok(PrimitiveType::U32), + Value::U64(_) => Ok(PrimitiveType::U64), + Value::Function(name, _) => { + Err(ValuePrimitiveTypeError::CannotConvertFunction(name.clone())) + } } } } @@ -61,8 +72,14 @@ impl From for PrimitiveType { } } +#[derive(thiserror::Error, Debug, Clone, PartialEq)] +pub enum UnknownPrimType { + #[error("Could not convert '{0}' into a primitive type")] + UnknownPrimType(String), +} + impl FromStr for PrimitiveType { - type Err = PrimOpError; + type Err = UnknownPrimType; fn from_str(s: &str) -> Result { match s { @@ -74,7 +91,7 @@ impl FromStr for PrimitiveType { "u16" => Ok(PrimitiveType::U16), "u32" => Ok(PrimitiveType::U32), "u64" => Ok(PrimitiveType::U64), - _ => Err(PrimOpError::UnknownPrimType(s.to_string())), + _ => Err(UnknownPrimType::UnknownPrimType(s.to_owned())), } } } @@ -152,7 +169,7 @@ impl PrimitiveType { (PrimitiveType::I64, Value::I64(x)) => Ok(Value::I64(*x)), _ => Err(PrimOpError::UnsafeCast { - from: source.into(), + from: PrimitiveType::try_from(source)?, to: *self, }), } diff --git a/src/eval/value.rs b/src/eval/value.rs index 12f3746..3634a2f 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -1,11 +1,13 @@ -use std::fmt::Display; +use super::EvalError; +use std::fmt; +use std::rc::Rc; /// Values in the interpreter. /// /// Yes, this is yet another definition of a structure called `Value`, which /// are almost entirely identical. However, it's nice to have them separated /// by type so that we don't mix them up. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone)] pub enum Value { I8(i8), I16(i16), @@ -15,19 +17,79 @@ pub enum Value { U16(u16), U32(u32), U64(u64), + Function( + Option, + Rc) -> Result>, + ), } -impl Display for Value { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +fn format_value(value: &Value, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match value { + Value::I8(x) => write!(f, "{}i8", x), + Value::I16(x) => write!(f, "{}i16", x), + Value::I32(x) => write!(f, "{}i32", x), + Value::I64(x) => write!(f, "{}i64", x), + Value::U8(x) => write!(f, "{}u8", x), + Value::U16(x) => write!(f, "{}u16", x), + Value::U32(x) => write!(f, "{}u32", x), + Value::U64(x) => write!(f, "{}u64", x), + Value::Function(Some(name), _) => write!(f, "", name), + Value::Function(None, _) => write!(f, ""), + } +} + +impl fmt::Debug for Value { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + format_value(self, f) + } +} + +impl fmt::Display for Value { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + format_value(self, f) + } +} + +impl PartialEq for Value { + fn eq(&self, other: &Self) -> bool { match self { - Value::I8(x) => write!(f, "{}i8", x), - Value::I16(x) => write!(f, "{}i16", x), - Value::I32(x) => write!(f, "{}i32", x), - Value::I64(x) => write!(f, "{}i64", x), - Value::U8(x) => write!(f, "{}u8", x), - Value::U16(x) => write!(f, "{}u16", x), - Value::U32(x) => write!(f, "{}u32", x), - Value::U64(x) => write!(f, "{}u64", x), + Value::I8(x) => match other { + Value::I8(y) => x == y, + _ => false, + }, + Value::I16(x) => match other { + Value::I16(y) => x == y, + _ => false, + }, + Value::I32(x) => match other { + Value::I32(y) => x == y, + _ => false, + }, + Value::I64(x) => match other { + Value::I64(y) => x == y, + _ => false, + }, + Value::U8(x) => match other { + Value::U8(y) => x == y, + _ => false, + }, + Value::U16(x) => match other { + Value::U16(y) => x == y, + _ => false, + }, + Value::U32(x) => match other { + Value::U32(y) => x == y, + _ => false, + }, + Value::U64(x) => match other { + Value::U64(y) => x == y, + _ => false, + }, + Value::Function(Some(x), _) => match other { + Value::Function(Some(y), _) => x == y, + _ => false, + }, + Value::Function(None, _) => false, } } } diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 6a7b62d..c63f5db 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -1,6 +1,7 @@ use crate::{ eval::PrimitiveType, syntax::{self, ConstantType, Location}, + util::pretty::{pretty_comma_separated, PrettySymbol}, }; use internment::ArcIntern; use pretty::{BoxAllocator, DocAllocator, Pretty}; @@ -87,20 +88,33 @@ where { fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { match self { - TopLevel::Function(name, args, stmts, body) => allocator - .text("function") - .append(allocator.space()) - .append(allocator.text(name.as_ref().to_string())) - .append( - allocator - .intersperse( - args.iter().map(|x| allocator.text(x.as_ref().to_string())), - ", ", + TopLevel::Function(name, args, stmts, expr) => { + let base = allocator + .text("function") + .append(allocator.space()) + .append(allocator.text(name.as_ref().to_string())) + .append(allocator.space()) + .append( + pretty_comma_separated( + allocator, + &args.iter().map(PrettySymbol::from).collect(), ) .parens(), - ) - .append(allocator.space()) - .append(body.pretty(allocator)), + ) + .append(allocator.space()); + + let mut body = allocator.nil(); + for stmt in stmts { + body = body + .append(stmt.pretty(allocator)) + .append(allocator.text(";")) + .append(allocator.hardline()); + } + body = body.append(expr.pretty(allocator)); + body = body.append(allocator.hardline()); + body = body.braces(); + base.append(body) + } TopLevel::Statement(stmt) => stmt.pretty(allocator), } @@ -389,6 +403,14 @@ where fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { match self { Type::Primitive(pt) => allocator.text(format!("{}", pt)), + Type::Function(args, rettype) => { + pretty_comma_separated(allocator, &args.iter().collect()) + .parens() + .append(allocator.space()) + .append(allocator.text("->")) + .append(allocator.space()) + .append(rettype.pretty(allocator)) + } } } } @@ -397,6 +419,18 @@ impl fmt::Display for Type { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Type::Primitive(pt) => pt.fmt(f), + Type::Function(args, ret) => { + write!(f, "(")?; + let mut argiter = args.iter().peekable(); + while let Some(arg) = argiter.next() { + arg.fmt(f)?; + if argiter.peek().is_some() { + write!(f, ",")?; + } + } + write!(f, "->")?; + ret.fmt(f) + } } } } diff --git a/src/ir/eval.rs b/src/ir/eval.rs index 46123f3..93d56b0 100644 --- a/src/ir/eval.rs +++ b/src/ir/eval.rs @@ -1,8 +1,7 @@ +use super::{Primitive, Type, ValueOrRef}; use crate::eval::{EvalEnvironment, EvalError, Value}; use crate::ir::{Expression, Program, Statement, TopLevel}; -use super::{Primitive, Type, ValueOrRef}; - impl Program { /// Evaluate the program, returning either an error or a string containing everything /// the program printed out. @@ -14,7 +13,7 @@ impl Program { for stmt in self.items.iter() { match stmt { - TopLevel::Function(_, _, _) => unimplemented!(), + TopLevel::Function(_, _, _, _) => unimplemented!(), TopLevel::Statement(Statement::Binding(_, name, _, value)) => { let actual_value = value.eval(&env)?; @@ -43,6 +42,7 @@ impl Expression { match t { Type::Primitive(pt) => Ok(pt.safe_cast(&value)?), + Type::Function(_, _) => Err(EvalError::CastToFunction(t.to_string())), } } diff --git a/src/ir/strings.rs b/src/ir/strings.rs index 6326672..dc05432 100644 --- a/src/ir/strings.rs +++ b/src/ir/strings.rs @@ -21,7 +21,12 @@ impl Program { impl TopLevel { fn register_strings(&self, string_set: &mut HashSet>) { match self { - TopLevel::Function(_, _, body) => body.register_strings(string_set), + TopLevel::Function(_, _, stmts, body) => { + for stmt in stmts.iter() { + stmt.register_strings(string_set); + } + body.register_strings(string_set); + } TopLevel::Statement(stmt) => stmt.register_strings(string_set), } } diff --git a/src/syntax.rs b/src/syntax.rs index 2a8acc8..a33119b 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -29,7 +29,7 @@ use logos::Logos; pub mod arbitrary; mod ast; -mod eval; +pub mod eval; mod location; mod tokens; lalrpop_mod!( diff --git a/src/syntax/location.rs b/src/syntax/location.rs index 5acd070..09e6e0c 100644 --- a/src/syntax/location.rs +++ b/src/syntax/location.rs @@ -85,9 +85,9 @@ impl Location { /// the user with some guidance. That being said, you still might want to add /// even more information to ut, using [`Diagnostic::with_labels`], /// [`Diagnostic::with_notes`], or [`Diagnostic::with_code`]. - pub fn labelled_error(&self, msg: &str) -> Diagnostic { + pub fn labelled_error>(&self, msg: T) -> Diagnostic { Diagnostic::error().with_labels(vec![ - Label::primary(self.file_idx, self.location.clone()).with_message(msg) + Label::primary(self.file_idx, self.location.clone()).with_message(msg.as_ref()) ]) } diff --git a/src/syntax/validate.rs b/src/syntax/validate.rs index f175eed..46c471c 100644 --- a/src/syntax/validate.rs +++ b/src/syntax/validate.rs @@ -123,7 +123,7 @@ impl TopLevel { for arg in arguments.iter() { bound_variables.insert(arg.name.clone(), arg.location.clone()); } - let result = body.validate(&bound_variables); + let result = body.validate(bound_variables); bound_variables.release_scope(); result } diff --git a/src/type_infer.rs b/src/type_infer.rs index 0402998..08cd2da 100644 --- a/src/type_infer.rs +++ b/src/type_infer.rs @@ -47,6 +47,6 @@ proptest::proptest! { let syntax_result = input.eval(); let ir = input.type_infer().expect("arbitrary should generate type-safe programs"); let ir_result = ir.eval(); - proptest::prop_assert_eq!(syntax_result, ir_result); + proptest::prop_assert!(syntax_result.eq(&ir_result)); } } diff --git a/src/type_infer/ast.rs b/src/type_infer/ast.rs index daffbf7..21eb2fc 100644 --- a/src/type_infer/ast.rs +++ b/src/type_infer/ast.rs @@ -9,6 +9,7 @@ pub use crate::ir::ast::Primitive; use crate::{ eval::PrimitiveType, syntax::{self, ConstantType, Location}, + util::pretty::{pretty_comma_separated, PrettySymbol}, }; use internment::ArcIntern; use pretty::{DocAllocator, Pretty}; @@ -87,10 +88,7 @@ where .append( pretty_comma_separated( allocator, - &args - .iter() - .map(|x| allocator.text(x.as_ref().to_string())) - .collect(), + &args.iter().map(PrettySymbol::from).collect(), ) .parens(), ) @@ -408,16 +406,3 @@ pub fn gentype() -> Type { Type::Variable(Location::manufactured(), name) } - -fn pretty_comma_separated<'a, D, A, P>( - allocator: &'a D, - args: &Vec

, -) -> pretty::DocBuilder<'a, D, A> -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, - P: Pretty<'a, D, A>, -{ - let individuals = args.iter().map(|x| x.pretty(allocator)); - allocator.intersperse(individuals, ", ") -} diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index bff8c2a..e6dc161 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -65,7 +65,7 @@ pub fn convert_top_level( // Now let's bind these types into the environment. First, we bind our function // namae to the function type we just generated. - bindings.insert(funname, funtype); + bindings.insert(funname.clone(), funtype); // And then we attach the argument names to the argument types. (We have to go // convert all the names, first.) let iargs: Vec> = @@ -291,7 +291,7 @@ fn finalize_name( renames: &mut HashMap, ArcIntern>, name: syntax::Name, ) -> ArcIntern { - if bindings.contains_key(&ArcIntern::new(name.name)) { + if bindings.contains_key(&ArcIntern::new(name.name.clone())) { let new_name = ir::gensym(&name.name); renames.insert(ArcIntern::new(name.name.to_string()), new_name.clone()); new_name diff --git a/src/type_infer/solve.rs b/src/type_infer/solve.rs index ec33384..76d072b 100644 --- a/src/type_infer/solve.rs +++ b/src/type_infer/solve.rs @@ -102,11 +102,19 @@ pub enum TypeInferenceError { /// The user provide a constant that is too large for its inferred type. ConstantTooLarge(Location, PrimitiveType, u64), /// The two types needed to be equivalent, but weren't. - NotEquivalent(Location, PrimitiveType, PrimitiveType), + NotEquivalent(Location, Type, Type), /// We cannot safely cast the first type to the second type. CannotSafelyCast(Location, PrimitiveType, PrimitiveType), /// The primitive invocation provided the wrong number of arguments. WrongPrimitiveArity(Location, ir::Primitive, usize, usize, usize), + /// We cannot cast between function types at the moment. + CannotCastBetweenFunctinoTypes(Location, Type, Type), + /// We cannot cast from a function type to something else. + CannotCastFromFunctionType(Location, Type), + /// We cannot cast to a function type from something else. + CannotCastToFunctionType(Location, Type), + /// We cannot turn a number into a function. + CannotMakeNumberAFunction(Location, Type, Option), /// We had a constraint we just couldn't solve. CouldNotSolve(Constraint), } @@ -142,6 +150,29 @@ impl From for Diagnostic { prim, observed )), + TypeInferenceError::CannotCastBetweenFunctinoTypes(loc, t1, t2) => loc + .labelled_error("cannot cast between function types") + .with_message(format!( + "tried to cast from {} to {}", + t1, t2, + )), + TypeInferenceError::CannotCastFromFunctionType(loc, t) => loc + .labelled_error("cannot cast from a function type to anything else") + .with_message(format!( + "function type was {}", t, + )), + TypeInferenceError::CannotCastToFunctionType(loc, t) => loc + .labelled_error("cannot cast to a function type") + .with_message(format!( + "function type was {}", t, + )), + TypeInferenceError::CannotMakeNumberAFunction(loc, t, val) => loc + .labelled_error(if let Some(val) = val { + format!("cannot turn {} into a function", val) + } else { + "cannot use a constant as a function type".to_string() + }) + .with_message(format!("function type was {}", t)), TypeInferenceError::CouldNotSolve(Constraint::CanCastTo(loc, a, b)) => { loc.labelled_error("internal error").with_message(format!( "could not determine if it was safe to cast from {} to {:#?}", @@ -236,12 +267,12 @@ pub fn solve_constraints( // Currently, all of our types are printable Constraint::Printable(_loc, _ty) => changed_something = true, - // Case #1: We have two primitive types. If they're equal, we've discharged this + // Case #1a: We have two primitive types. If they're equal, we've discharged this // constraint! We can just continue. If they're not equal, add an error and then // see what else we come up with. - Constraint::Equivalent(loc, Type::Primitive(t1), Type::Primitive(t2)) => { - if t1 != t2 { - errors.push(TypeInferenceError::NotEquivalent(loc, t1, t2)); + Constraint::Equivalent(loc, a @ Type::Primitive(_), b @ Type::Primitive(_)) => { + if a != b { + errors.push(TypeInferenceError::NotEquivalent(loc, a, b)); } changed_something = true; } @@ -257,7 +288,11 @@ pub fn solve_constraints( resolutions.insert(name, t); } Some(t2) if &t == t2 => {} - Some(t2) => errors.push(TypeInferenceError::NotEquivalent(loc, t, *t2)), + Some(t2) => errors.push(TypeInferenceError::NotEquivalent( + loc, + Type::Primitive(t), + Type::Primitive(*t2), + )), } changed_something = true; } @@ -284,11 +319,61 @@ pub fn solve_constraints( changed_something = true; } (Some(pt1), Some(pt2)) => { - errors.push(TypeInferenceError::NotEquivalent(loc.clone(), *pt1, *pt2)); + errors.push(TypeInferenceError::NotEquivalent( + loc.clone(), + Type::Primitive(*pt1), + Type::Primitive(*pt2), + )); changed_something = true; } }, + // Case #4: Like primitives, but for function types. This is a little complicated, because + // we first want to resolve all the type variables in the two types, and then see if they're + // equivalent. Fortunately, though, we can cheat a bit. What we're going to do is first see + // if the two types have the same arity (the same number of arguments). If not, we know the + // types don't match. If they do, then we're going to just turn this into a bunch of different + // equivalence constraints by matching up each of the arguments as well as the return type, and + // then restarting the type checking loop. That will cause any of those type variables to be + // handled appropriately. This even works recursively, so we can support arbitrarily nested + // function types. + Constraint::Equivalent( + loc, + ref a @ Type::Function(ref args1, ref ret1), + ref b @ Type::Function(ref args2, ref ret2), + ) => { + if args1.len() != args2.len() { + errors.push(TypeInferenceError::NotEquivalent( + loc.clone(), + a.clone(), + b.clone(), + )); + } else { + for (left, right) in args1.iter().zip(args2.iter()) { + constraint_db.push(Constraint::Equivalent( + loc.clone(), + left.clone(), + right.clone(), + )); + } + } + + constraint_db.push(Constraint::Equivalent( + loc, + ret1.as_ref().clone(), + ret2.as_ref().clone(), + )); + + changed_something = true; + } + + // Case #5: They're just totally the wrong types. In which case, we're done with + // this one; emit the error and drop the constraint. + Constraint::Equivalent(loc, a, b) => { + errors.push(TypeInferenceError::NotEquivalent(loc, a, b)); + changed_something = true; + } + // Make sure that the provided number fits within the provided constant type. For the // moment, we're going to call an error here a failure, although this could be a // warning in the future. @@ -320,6 +405,15 @@ pub fn solve_constraints( } } + // Function types definitely do not fit in numeric types + Constraint::FitsInNumType(loc, t @ Type::Function(_, _), val) => { + errors.push(TypeInferenceError::CannotMakeNumberAFunction( + loc, + t.clone(), + Some(val), + )); + } + // If the left type in a "can cast to" check is a variable, let's see if we can advance // it into something more tangible Constraint::CanCastTo(loc, Type::Variable(vloc, var), to_type) => { @@ -373,6 +467,36 @@ pub fn solve_constraints( changed_something = true; } + // If either type is a function type, then we can only cast if the two types + // are equivalent. + Constraint::CanCastTo( + loc, + t1 @ Type::Function(_, _), + t2 @ Type::Function(_, _), + ) => { + if t1 != t2 { + errors.push(TypeInferenceError::CannotCastBetweenFunctinoTypes( + loc, + t1.clone(), + t2.clone(), + )); + } + changed_something = true; + } + + Constraint::CanCastTo(loc, t @ Type::Function(_, _), Type::Primitive(_)) => { + errors.push(TypeInferenceError::CannotCastFromFunctionType( + loc, + t.clone(), + )); + changed_something = true; + } + + Constraint::CanCastTo(loc, Type::Primitive(_), t @ Type::Function(_, _)) => { + errors.push(TypeInferenceError::CannotCastToFunctionType(loc, t.clone())); + changed_something = true; + } + // As per usual, if we're trying to test if a type variable is numeric, first // we try to advance it to a primitive Constraint::NumericType(loc, Type::Variable(vloc, var)) => { @@ -392,6 +516,16 @@ pub fn solve_constraints( changed_something = true; } + // But functions are definitely not numbers + Constraint::NumericType(loc, t @ Type::Function(_, _)) => { + errors.push(TypeInferenceError::CannotMakeNumberAFunction( + loc, + t.clone(), + None, + )); + changed_something = true; + } + // As per usual, if we're trying to test if a type variable is numeric, first // we try to advance it to a primitive Constraint::ConstantNumericType(loc, Type::Variable(vloc, var)) => { @@ -414,6 +548,16 @@ pub fn solve_constraints( changed_something = true; } + // But functions are definitely not numbers + Constraint::ConstantNumericType(loc, t @ Type::Function(_, _)) => { + errors.push(TypeInferenceError::CannotMakeNumberAFunction( + loc, + t.clone(), + None, + )); + changed_something = true; + } + // OK, this one could be a little tricky if we tried to do it all at once, but // instead what we're going to do here is just use this constraint to generate // a bunch more constraints, and then go have the engine solve those. The only diff --git a/src/util.rs b/src/util.rs index b7271cc..5e15869 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1 +1,2 @@ +pub mod pretty; pub mod scoped_map; diff --git a/src/util/pretty.rs b/src/util/pretty.rs new file mode 100644 index 0000000..e5c478a --- /dev/null +++ b/src/util/pretty.rs @@ -0,0 +1,47 @@ +use internment::ArcIntern; +use pretty::{DocAllocator, Pretty}; + +#[derive(Clone)] +pub struct PrettySymbol { + name: ArcIntern, +} + +impl<'a> From<&'a ArcIntern> for PrettySymbol { + fn from(value: &'a ArcIntern) -> Self { + PrettySymbol { name: value.clone() } + } +} + +impl<'a, D, A> Pretty<'a, D, A> for PrettySymbol +where + A: 'a, + D: ?Sized + DocAllocator<'a, A>, +{ + fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { + allocator.text(self.name.as_ref().to_string()) + } +} + +impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b PrettySymbol +where + A: 'a, + D: ?Sized + DocAllocator<'a, A>, +{ + fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { + allocator.text(self.name.as_ref().to_string()) + } +} + +#[allow(clippy::ptr_arg)] +pub fn pretty_comma_separated<'a, D, A, P>( + allocator: &'a D, + args: &Vec

, +) -> pretty::DocBuilder<'a, D, A> +where + A: 'a, + D: ?Sized + DocAllocator<'a, A>, + P: Pretty<'a, D, A> + Clone, +{ + let individuals = args.iter().map(|x| x.clone().pretty(allocator)); + allocator.intersperse(individuals, ", ") +} diff --git a/src/util/scoped_map.rs b/src/util/scoped_map.rs index dc8a9a1..69f442c 100644 --- a/src/util/scoped_map.rs +++ b/src/util/scoped_map.rs @@ -5,6 +5,12 @@ pub struct ScopedMap { scopes: Vec>, } +impl Default for ScopedMap { + fn default() -> Self { + ScopedMap::new() + } +} + impl ScopedMap { /// Generate a new scoped map. /// -- 2.53.0 From 93cac44a99cd415b47b2e380c7c54893801e4121 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 2 Dec 2023 22:38:44 -0800 Subject: [PATCH 07/59] checkpoint; builds again --- src/backend.rs | 6 +- src/backend/error.rs | 9 + src/backend/eval.rs | 68 ++++- src/backend/into_crane.rs | 611 +++++++++++++++++++++++++------------ src/compiler.rs | 2 +- src/eval/primtype.rs | 22 +- src/ir/ast.rs | 325 ++++++++++++++------ src/ir/eval.rs | 35 ++- src/ir/strings.rs | 31 +- src/repl.rs | 12 +- src/type_infer.rs | 6 +- src/type_infer/ast.rs | 408 ------------------------- src/type_infer/convert.rs | 434 +++++++++++++------------- src/type_infer/finalize.rs | 246 +++++++-------- src/type_infer/solve.rs | 175 ++++++----- src/util/pretty.rs | 4 +- 16 files changed, 1200 insertions(+), 1194 deletions(-) delete mode 100644 src/type_infer/ast.rs diff --git a/src/backend.rs b/src/backend.rs index 4ac8e83..c6f217b 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -39,6 +39,7 @@ use cranelift_codegen::{isa, settings}; use cranelift_jit::{JITBuilder, JITModule}; use cranelift_module::{default_libcall_names, DataDescription, DataId, FuncId, Linkage, Module}; use cranelift_object::{ObjectBuilder, ObjectModule}; +use internment::ArcIntern; use std::collections::HashMap; use target_lexicon::Triple; @@ -58,7 +59,7 @@ pub struct Backend { data_ctx: DataDescription, runtime_functions: RuntimeFunctions, defined_strings: HashMap, - defined_symbols: HashMap, + defined_symbols: HashMap, (DataId, ConstantType)>, output_buffer: Option, platform: Triple, } @@ -181,7 +182,8 @@ impl Backend { .declare_data(&name, Linkage::Export, true, false)?; self.module.define_data(id, &self.data_ctx)?; self.data_ctx.clear(); - self.defined_symbols.insert(name, (id, ctype)); + self.defined_symbols + .insert(ArcIntern::new(name), (id, ctype)); Ok(id) } diff --git a/src/backend/error.rs b/src/backend/error.rs index 7509da3..f7252e9 100644 --- a/src/backend/error.rs +++ b/src/backend/error.rs @@ -41,6 +41,8 @@ pub enum BackendError { Write(#[from] cranelift_object::object::write::Error), #[error("Invalid type cast from {from} to {to}")] InvalidTypeCast { from: PrimitiveType, to: Type }, + #[error("Unknown string constant '{0}")] + UnknownString(ArcIntern), } impl From for Diagnostic { @@ -69,6 +71,8 @@ impl From for Diagnostic { BackendError::InvalidTypeCast { from, to } => Diagnostic::error().with_message( format!("Internal error trying to cast from {} to {}", from, to), ), + BackendError::UnknownString(str) => Diagnostic::error() + .with_message(format!("Unknown string found trying to compile: '{}'", str)), } } } @@ -119,6 +123,11 @@ impl PartialEq for BackendError { } => from1 == from2 && to1 == to2, _ => false, }, + + BackendError::UnknownString(a) => match other { + BackendError::UnknownString(b) => a == b, + _ => false, + }, } } } diff --git a/src/backend/eval.rs b/src/backend/eval.rs index 78ed7a5..81129eb 100644 --- a/src/backend/eval.rs +++ b/src/backend/eval.rs @@ -1,12 +1,14 @@ use crate::backend::Backend; use crate::eval::EvalError; -use crate::ir::Program; +use crate::ir::{Expression, Program, TopLevel, Type}; #[cfg(test)] use crate::syntax::arbitrary::GenerationEnvironment; +use crate::syntax::Location; use cranelift_jit::JITModule; use cranelift_object::ObjectModule; #[cfg(test)] use proptest::arbitrary::Arbitrary; +use std::collections::HashMap; use std::path::Path; use target_lexicon::Triple; @@ -24,9 +26,36 @@ impl Backend { /// library do. So, if you're validating equivalence between them, you'll want to weed /// out examples that overflow/underflow before checking equivalence. (This is the behavior /// of the built-in test systems.) - pub fn eval(program: Program) -> Result { + pub fn eval(program: Program) -> Result { let mut jitter = Backend::jit(Some(String::new()))?; - let function_id = jitter.compile_function("test", program)?; + let mut function_map = HashMap::new(); + let mut main_function_body = vec![]; + + for item in program.items { + match item { + TopLevel::Function(name, args, rettype, body) => { + let function_id = + jitter.compile_function(name.as_str(), args.as_slice(), rettype, body)?; + function_map.insert(name, function_id); + } + + TopLevel::Statement(stmt) => { + main_function_body.push(stmt); + } + } + } + + let main_function_body = Expression::Block( + Location::manufactured(), + Type::Primitive(crate::eval::PrimitiveType::Void), + main_function_body, + ); + let function_id = jitter.compile_function( + "___test_jit_eval___", + &[], + Type::Primitive(crate::eval::PrimitiveType::Void), + main_function_body, + )?; jitter.module.finalize_definitions()?; let compiled_bytes = jitter.bytes(function_id); let compiled_function = unsafe { std::mem::transmute::<_, fn() -> ()>(compiled_bytes) }; @@ -51,17 +80,44 @@ impl Backend { /// library do. So, if you're validating equivalence between them, you'll want to weed /// out examples that overflow/underflow before checking equivalence. (This is the behavior /// of the built-in test systems.) - pub fn eval(program: Program) -> Result { + pub fn eval(program: Program) -> Result { //use pretty::{Arena, Pretty}; //let allocator = Arena::<()>::new(); //program.pretty(&allocator).render(80, &mut std::io::stdout())?; - let mut backend = Self::object_file(Triple::host())?; + let mut function_map = HashMap::new(); + let mut main_function_body = vec![]; + + for item in program.items { + match item { + TopLevel::Function(name, args, rettype, body) => { + let function_id = + backend.compile_function(name.as_str(), args.as_slice(), rettype, body)?; + function_map.insert(name, function_id); + } + + TopLevel::Statement(stmt) => { + main_function_body.push(stmt); + } + } + } + + let main_function_body = Expression::Block( + Location::manufactured(), + Type::Primitive(crate::eval::PrimitiveType::Void), + main_function_body, + ); + let my_directory = tempfile::tempdir()?; let object_path = my_directory.path().join("object.o"); let executable_path = my_directory.path().join("test_executable"); - backend.compile_function("gogogo", program)?; + backend.compile_function( + "gogogo", + &[], + Type::Primitive(crate::eval::PrimitiveType::Void), + main_function_body, + )?; let bytes = backend.bytes()?; std::fs::write(&object_path, bytes)?; Self::link(&object_path, &executable_path)?; diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index a9989de..0f77339 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -1,15 +1,15 @@ use std::collections::HashMap; use crate::eval::PrimitiveType; -use crate::ir::{Expression, Primitive, Program, Statement, TopLevel, Type, Value, ValueOrRef}; -use crate::syntax::ConstantType; -use cranelift_codegen::entity::EntityRef; +use crate::ir::{Expression, Primitive, Program, TopLevel, Type, Value, ValueOrRef, Variable}; +use crate::syntax::{ConstantType, Location}; +use crate::util::scoped_map::ScopedMap; use cranelift_codegen::ir::{ - self, entities, types, Function, GlobalValue, InstBuilder, MemFlags, Signature, UserFuncName, + self, entities, types, AbiParam, Function, GlobalValue, InstBuilder, Signature, UserFuncName, }; use cranelift_codegen::isa::CallConv; use cranelift_codegen::Context; -use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; +use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use cranelift_module::{FuncId, Linkage, Module}; use internment::ArcIntern; @@ -24,25 +24,101 @@ use crate::backend::Backend; /// This just a handy type alias to avoid a lot of confusion in the functions. type StringTable = HashMap, GlobalValue>; +/// When we're talking about variables, it's handy to just have a table that points +/// from a variable to "what to do if you want to reference this variable", which is +/// agnostic about whether the variable is local, global, an argument, etc. Since +/// the type of that function is a little bit annoying, we summarize it here. +struct ReferenceBuilder { + ir_type: ConstantType, + cranelift_type: cranelift_codegen::ir::Type, + local_data: GlobalValue, +} + +impl ReferenceBuilder { + fn refer_to(&self, builder: &mut FunctionBuilder) -> (entities::Value, ConstantType) { + let value = builder.ins().symbol_value(self.cranelift_type, self.local_data); + (value, self.ir_type) + } +} + impl Backend { - /// Compile the given `Program` into a function with the given name. + /// Translate the given IR type into an ABI parameter type for cranelift, as + /// best as possible. + fn translate_type(&self, t: &Type) -> AbiParam { + let (value_type, extension) = match t { + Type::Function(_, _) => ( + types::Type::triple_pointer_type(&self.platform), + ir::ArgumentExtension::None, + ), + Type::Primitive(PrimitiveType::Void) => (types::I8, ir::ArgumentExtension::None), // FIXME? + Type::Primitive(PrimitiveType::I8) => (types::I8, ir::ArgumentExtension::Sext), + Type::Primitive(PrimitiveType::I16) => (types::I16, ir::ArgumentExtension::Sext), + Type::Primitive(PrimitiveType::I32) => (types::I32, ir::ArgumentExtension::Sext), + Type::Primitive(PrimitiveType::I64) => (types::I64, ir::ArgumentExtension::Sext), + Type::Primitive(PrimitiveType::U8) => (types::I8, ir::ArgumentExtension::Uext), + Type::Primitive(PrimitiveType::U16) => (types::I16, ir::ArgumentExtension::Uext), + Type::Primitive(PrimitiveType::U32) => (types::I32, ir::ArgumentExtension::Uext), + Type::Primitive(PrimitiveType::U64) => (types::I64, ir::ArgumentExtension::Uext), + }; + + AbiParam { + value_type, + purpose: ir::ArgumentPurpose::Normal, + extension, + } + } + + /// Compile the given program. /// - /// At some point, the use of `Program` is going to change; however, for the - /// moment, we have no notion of a function in our language so the whole input - /// is converted into a single output function. The type of the generated - /// function is, essentially, `fn() -> ()`: it takes no arguments and returns - /// no value. - /// - /// The function provided can then be either written to a file (if using a - /// static Cranelift backend) or executed directly (if using the Cranelift JIT). + /// The returned value is a `FuncId` that represents a function that runs all the statements + /// found in the program, which will be compiled using the given function name. (If there + /// are no such statements, the function will do nothing.) + pub fn compile_program( + &mut self, + function_name: &str, + program: Program, + ) -> Result { + let mut generated_body = vec![]; + + for item in program.items { + match item { + TopLevel::Function(name, args, rettype, body) => { + self.compile_function(name.as_str(), &args, rettype, body); + } + + TopLevel::Statement(stmt) => { + generated_body.push(stmt); + } + } + } + + let void = Type::Primitive(PrimitiveType::Void); + self.compile_function( + function_name, + &[], + void.clone(), + Expression::Block(Location::manufactured(), void, generated_body), + ) + } + + /// Compile the given function. pub fn compile_function( &mut self, function_name: &str, - mut program: Program, + arguments: &[(Variable, Type)], + return_type: Type, + body: Expression, ) -> Result { let basic_signature = Signature { - params: vec![], - returns: vec![], + params: arguments + .iter() + .map(|(_, t)| self.translate_type(t)) + .collect(), + returns: if return_type == Type::Primitive(PrimitiveType::Void) { + vec![] + } else { + vec![self.translate_type(&return_type)] + }, call_conv: CallConv::triple_default(&self.platform), }; @@ -63,13 +139,6 @@ impl Backend { let user_func_name = UserFuncName::user(0, func_id.as_u32()); ctx.func = Function::with_name_signature(user_func_name, basic_signature); - // We generate a table of every string that we use in the program, here. - // Cranelift is going to require us to have this in a particular structure - // (`GlobalValue`) so that we can reference them later, and it's going to - // be tricky to generate those on the fly. So we just generate the set we - // need here, and then have ir around in the table for later. - let string_table = self.build_string_table(&mut ctx.func, &program)?; - // In the future, we might want to see what runtime functions the function // we were given uses, and then only include those functions that we care // about. Presumably, we'd use some sort of lookup table like we do for @@ -82,25 +151,32 @@ impl Backend { &mut ctx.func, )?; - // In the case of the JIT, there may be symbols we've already defined outside - // the context of this particular `Progam`, which we might want to reference. - // Just like with strings, generating the `GlobalValue`s we need can potentially - // be a little tricky to do on the fly, so we generate the complete list right - // here and then use it later. - let pre_defined_symbols: HashMap = self - .defined_symbols - .iter() - .map(|(k, (v, t))| { - let local_data = self.module.declare_data_in_func(*v, &mut ctx.func); - (k.clone(), (local_data, *t)) - }) - .collect(); + // Let's start creating the variable table we'll use when we're dereferencing + // them later. This table is a little interesting because instead of pointing + // from data to data, we're going to point from data (the variable) to an + // action to take if we encounter that variable at some later point. This + // makes it nice and easy to have many different ways to access data, such + // as globals, function arguments, etc. + let mut variables: ScopedMap, ReferenceBuilder> = ScopedMap::new(); - // The last table we're going to need is our local variable table, to store - // variables used in this `Program` but not used outside of it. For whatever - // reason, Cranelift requires us to generate unique indexes for each of our - // variables; we just use a simple incrementing counter for that. - let mut variable_table = HashMap::new(); + // At the outer-most scope of things, we'll put global variables we've defined + // elsewhere in the program. + for (name, (data_id, ty)) in self.defined_symbols.iter() { + let local_data = self.module.declare_data_in_func(*data_id, &mut ctx.func); + let cranelift_type = ir::Type::from(*ty); + variables.insert( + name.clone(), + ReferenceBuilder { cranelift_type, local_data, ir_type: *ty }, + ); + } + + // Once we have these, we're going to actually push a level of scope and + // add our arguments. We push scope because if there happen to be any with + // the same name (their shouldn't be, but just in case), we want the arguments + // to win. + variables.new_scope(); + + // FIXME: Add arguments let mut next_var_num = 1; // Finally (!), we generate the function builder that we're going to use to @@ -114,98 +190,13 @@ impl Backend { let main_block = builder.create_block(); builder.switch_to_block(main_block); - // Compiling a function is just compiling each of the statements in order. - // At the moment, we do the pattern match for statements here, and then - // directly compile the statements. If/when we add more statement forms, - // this is likely to become more cumbersome, and we'll want to separate - // these off. But for now, given the amount of tables we keep around to track - // state, it's easier to just include them. - for item in program.items.drain(..) { - match item { - TopLevel::Function(_, _, _, _) => unimplemented!(), - - // Print statements are fairly easy to compile: we just lookup the - // output buffer, the address of the string to print, and the value - // of whatever variable we're printing. Then we just call print. - TopLevel::Statement(Statement::Print(ann, t, var)) => { - // Get the output buffer (or null) from our general compilation context. - let buffer_ptr = self.output_buffer_ptr(); - let buffer_ptr = builder.ins().iconst(types::I64, buffer_ptr as i64); - - // Get a reference to the string we want to print. - let local_name_ref = string_table.get(&var).unwrap(); - let name_ptr = builder.ins().symbol_value(types::I64, *local_name_ref); - - // Look up the value for the variable. Because this might be a - // global variable (and that requires special logic), we just turn - // this into an `Expression` and re-use the logic in that implementation. - let (val, vtype) = ValueOrRef::Ref(ann, t, var).into_crane( - &mut builder, - &variable_table, - &pre_defined_symbols, - )?; - - let vtype_repr = builder.ins().iconst(types::I64, vtype as i64); - - let casted_val = match vtype { - ConstantType::U64 | ConstantType::I64 => val, - ConstantType::I8 | ConstantType::I16 | ConstantType::I32 => { - builder.ins().sextend(types::I64, val) - } - ConstantType::U8 | ConstantType::U16 | ConstantType::U32 => { - builder.ins().uextend(types::I64, val) - } - }; - - // Finally, we can generate the call to print. - builder.ins().call( - print_func_ref, - &[buffer_ptr, name_ptr, vtype_repr, casted_val], - ); - } - - // Variable binding is a little more con - TopLevel::Statement(Statement::Binding(_, var_name, _, value)) => { - // Kick off to the `Expression` implementation to see what value we're going - // to bind to this variable. - let (val, etype) = - value.into_crane(&mut builder, &variable_table, &pre_defined_symbols)?; - - // Now the question is: is this a local variable, or a global one? - if let Some((global_id, ctype)) = pre_defined_symbols.get(var_name.as_str()) { - // It's a global variable! In this case, we assume that someone has already - // dedicated some space in memory to store this value. We look this location - // up, and then tell Cranelift to store the value there. - assert_eq!(etype, *ctype); - let val_ptr = builder - .ins() - .symbol_value(ir::Type::from(*ctype), *global_id); - builder.ins().store(MemFlags::new(), val, val_ptr, 0); - } else { - // It's a local variable! In this case, we need to allocate a new Cranelift - // `Variable` for this variable, which we do using our `next_var_num` counter. - // (While we're doing this, we also increment `next_var_num`, so that we get - // a fresh `Variable` next time. This is one of those very narrow cases in which - // I wish Rust had an increment expression.) - let var = Variable::new(next_var_num); - next_var_num += 1; - - // We can add the variable directly to our local variable map; it's `Copy`. - variable_table.insert(var_name, (var, etype)); - - // Now we tell Cranelift about our new variable! - builder.declare_var(var, ir::Type::from(etype)); - builder.def_var(var, val); - } - } - } - } + let (value, _) = self.compile_expression(body, &mut variables, &mut builder)?; // Now that we're done, inject a return function (one with no actual value; basically // the equivalent of Rust's `return;`). We then seal the block (which lets Cranelift // know that the block is done), and then finalize the function (which lets Cranelift // know we're done with the function). - builder.ins().return_(&[]); + builder.ins().return_(&[value]); builder.seal_block(main_block); builder.finalize(); @@ -219,45 +210,18 @@ impl Backend { Ok(func_id) } - // Build the string table for use in referencing strings later. - // - // This function is slightly smart, in that it only puts strings in the table that - // are used by the `Program`. (Thanks to `Progam::strings()`!) If the strings have - // been declared globally, via `Backend::define_string()`, we will re-use that data. - // Otherwise, this will define the string for you. - fn build_string_table( + /// Compile an expression, returning the Cranelift Value for the expression and + /// its type. + fn compile_expression( &mut self, - func: &mut Function, - program: &Program, - ) -> Result { - let mut string_table = HashMap::new(); - - for interned_value in program.strings().drain() { - let global_id = match self.defined_strings.get(interned_value.as_str()) { - Some(x) => *x, - None => self.define_string(interned_value.as_str())?, - }; - let local_data = self.module.declare_data_in_func(global_id, func); - string_table.insert(interned_value, local_data); - } - - Ok(string_table) - } -} - -impl Expression { - fn into_crane( - self, + expr: Expression, + variables: &mut ScopedMap, builder: &mut FunctionBuilder, - local_variables: &HashMap, (Variable, ConstantType)>, - global_variables: &HashMap, ) -> Result<(entities::Value, ConstantType), BackendError> { - match self { - Expression::Atomic(x) => x.into_crane(builder, local_variables, global_variables), - - Expression::Cast(_, target_type, expr) => { - let (val, val_type) = - expr.into_crane(builder, local_variables, global_variables)?; + match expr { + Expression::Atomic(x) => self.compile_value_or_ref(x, variables, builder), + Expression::Cast(_, target_type, valref) => { + let (val, val_type) = self.compile_value_or_ref(valref, variables, builder)?; match (val_type, &target_type) { (ConstantType::I8, Type::Primitive(PrimitiveType::I8)) => Ok((val, val_type)), @@ -325,7 +289,7 @@ impl Expression { for val in vals.drain(..) { let (compiled, compiled_type) = - val.into_crane(builder, local_variables, global_variables)?; + self.compile_value_or_ref(val, variables, builder)?; if let Some(leftmost_type) = first_type { assert_eq!(leftmost_type, compiled_type); @@ -355,22 +319,79 @@ impl Expression { Primitive::Divide => Ok((builder.ins().udiv(values[0], values[1]), first_type)), } } + + Expression::Block(_, _, mut exprs) => match exprs.pop() { + None => Ok((builder.ins().iconst(types::I8, 0), ConstantType::I8)), + Some(last) => { + for inner in exprs { + // we can ignore all of these return values and such, because we + // don't actually use them anywhere + self.compile_expression(inner, variables, builder); + } + // instead, we just return the last one + self.compile_expression(last, variables, builder) + } + }, + + Expression::Print(ann, var) => { + // Get the output buffer (or null) from our general compilation context. + let buffer_ptr = self.output_buffer_ptr(); + let buffer_ptr = builder.ins().iconst(types::I64, buffer_ptr as i64); + + // Get a reference to the string we want to print. + let string_data_id = self + .defined_strings + .get(var.as_ref()) + .ok_or_else(|| BackendError::UnknownString(var.clone()))?; + let local_name_ref = self + .module + .declare_data_in_func(*string_data_id, builder.func); + let name_ptr = builder.ins().symbol_value(types::I64, local_name_ref); + + // Look up the value for the variable. Because this might be a + // global variable (and that requires special logic), we just turn + // this into an `Expression` and re-use the logic in that implementation. + let fake_ref = ValueOrRef::Ref(ann, Type::Primitive(PrimitiveType::U8), var); + let (val, vtype) = self.compile_value_or_ref(fake_ref, variables, builder)?; + + let vtype_repr = builder.ins().iconst(types::I64, vtype as i64); + + let casted_val = match vtype { + ConstantType::U64 | ConstantType::I64 => val, + ConstantType::I8 | ConstantType::I16 | ConstantType::I32 => { + builder.ins().sextend(types::I64, val) + } + ConstantType::U8 | ConstantType::U16 | ConstantType::U32 => { + builder.ins().uextend(types::I64, val) + } + }; + + // Finally, we can generate the call to print. + let print_func_ref = self.runtime_functions.include_runtime_function( + "print", + &mut self.module, + builder.func, + )?; + builder.ins().call( + print_func_ref, + &[buffer_ptr, name_ptr, vtype_repr, casted_val], + ); + Ok((builder.ins().iconst(types::I8, 0), ConstantType::I8)) + } + + Expression::Bind(_, _, _, _) => unimplemented!(), } } -} -// Just to avoid duplication, this just leverages the `From` trait implementation -// for `ValueOrRef` to compile this via the `Expression` logic, above. -impl ValueOrRef { - fn into_crane( - self, + /// Compile a value or reference into Cranelift, returning the Cranelift Value for + /// the expression and its type. + fn compile_value_or_ref( + &self, + valref: ValueOrRef, + variables: &ScopedMap, builder: &mut FunctionBuilder, - local_variables: &HashMap, (Variable, ConstantType)>, - global_variables: &HashMap, ) -> Result<(entities::Value, ConstantType), BackendError> { - match self { - // Values are pretty straightforward to compile, mostly because we only - // have one type of variable, and it's an integer type. + match valref { ValueOrRef::Value(_, _, val) => match val { Value::I8(_, v) => { Ok((builder.ins().iconst(types::I8, v as i64), ConstantType::I8)) @@ -400,31 +421,217 @@ impl ValueOrRef { ConstantType::U64, )), }, - - ValueOrRef::Ref(_, _, name) => { - // first we see if this is a local variable (which is nicer, from an - // optimization point of view.) - if let Some((local_var, etype)) = local_variables.get(&name) { - return Ok((builder.use_var(*local_var), *etype)); - } - - // then we check to see if this is a global reference, which requires us to - // first lookup where the value is stored, and then load it. - if let Some((global_var, etype)) = global_variables.get(name.as_ref()) { - let cranelift_type = ir::Type::from(*etype); - let val_ptr = builder.ins().symbol_value(cranelift_type, *global_var); - return Ok(( - builder - .ins() - .load(cranelift_type, MemFlags::new(), val_ptr, 0), - *etype, - )); - } - - // this should never happen, because we should have made sure that there are - // no unbound variables a long time before this. but still ... - Err(BackendError::VariableLookupFailure(name)) - } + ValueOrRef::Ref(_, _, name) => match variables.get(&name) { + None => Err(BackendError::VariableLookupFailure(name)), + Some(x) => Ok(x.refer_to(builder)), + }, } } + + // Compiling a function is just compiling each of the statements in order. + // At the moment, we do the pattern match for statements here, and then + // directly compile the statements. If/when we add more statement forms, + // this is likely to become more cumbersome, and we'll want to separate + // these off. But for now, given the amount of tables we keep around to track + // state, it's easier to just include them. + // for item in program.items.drain(..) { + // match item { + // TopLevel::Function(_, _, _) => unimplemented!(), + // + // // Print statements are fairly easy to compile: we just lookup the + // // output buffer, the address of the string to print, and the value + // // of whatever variable we're printing. Then we just call print. + // TopLevel::Statement(Statement::Print(ann, t, var)) => { + // // Get the output buffer (or null) from our general compilation context. + // let buffer_ptr = self.output_buffer_ptr(); + // let buffer_ptr = builder.ins().iconst(types::I64, buffer_ptr as i64); + // + // // Get a reference to the string we want to print. + // let local_name_ref = string_table.get(&var).unwrap(); + // let name_ptr = builder.ins().symbol_value(types::I64, *local_name_ref); + // + // // Look up the value for the variable. Because this might be a + // // global variable (and that requires special logic), we just turn + // // this into an `Expression` and re-use the logic in that implementation. + // let (val, vtype) = ValueOrRef::Ref(ann, t, var).into_crane( + // &mut builder, + // &variable_table, + // &pre_defined_symbols, + // )?; + // + // let vtype_repr = builder.ins().iconst(types::I64, vtype as i64); + // + // let casted_val = match vtype { + // ConstantType::U64 | ConstantType::I64 => val, + // ConstantType::I8 | ConstantType::I16 | ConstantType::I32 => { + // builder.ins().sextend(types::I64, val) + // } + // ConstantType::U8 | ConstantType::U16 | ConstantType::U32 => { + // builder.ins().uextend(types::I64, val) + // } + // }; + // + // // Finally, we can generate the call to print. + // builder.ins().call( + // print_func_ref, + // &[buffer_ptr, name_ptr, vtype_repr, casted_val], + // ); + // } + // + // // Variable binding is a little more con + // TopLevel::Statement(Statement::Binding(_, var_name, _, value)) => { + // // Kick off to the `Expression` implementation to see what value we're going + // // to bind to this variable. + // let (val, etype) = + // value.into_crane(&mut builder, &variable_table, &pre_defined_symbols)?; + // + // // Now the question is: is this a local variable, or a global one? + // if let Some((global_id, ctype)) = pre_defined_symbols.get(var_name.as_str()) { + // // It's a global variable! In this case, we assume that someone has already + // // dedicated some space in memory to store this value. We look this location + // // up, and then tell Cranelift to store the value there. + // assert_eq!(etype, *ctype); + // let val_ptr = builder + // .ins() + // .symbol_value(ir::Type::from(*ctype), *global_id); + // builder.ins().store(MemFlags::new(), val, val_ptr, 0); + // } else { + // // It's a local variable! In this case, we need to allocate a new Cranelift + // // `Variable` for this variable, which we do using our `next_var_num` counter. + // // (While we're doing this, we also increment `next_var_num`, so that we get + // // a fresh `Variable` next time. This is one of those very narrow cases in which + // // I wish Rust had an increment expression.) + // let var = Variable::new(next_var_num); + // next_var_num += 1; + // + // // We can add the variable directly to our local variable map; it's `Copy`. + // variable_table.insert(var_name, (var, etype)); + // + // // Now we tell Cranelift about our new variable! + // builder.declare_var(var, ir::Type::from(etype)); + // builder.def_var(var, val); + // } + // } + // } + // } + + // Build the string table for use in referencing strings later. + // + // This function is slightly smart, in that it only puts strings in the table that + // are used by the `Program`. (Thanks to `Progam::strings()`!) If the strings have + // been declared globally, via `Backend::define_string()`, we will re-use that data. + // Otherwise, this will define the string for you. + // fn build_string_table( + // &mut self, + // func: &mut Function, + // program: &Expression, + // ) -> Result { + // let mut string_table = HashMap::new(); + // + // for interned_value in program.strings().drain() { + // let global_id = match self.defined_strings.get(interned_value.as_str()) { + // Some(x) => *x, + // None => self.define_string(interned_value.as_str())?, + // }; + // let local_data = self.module.declare_data_in_func(global_id, func); + // string_table.insert(interned_value, local_data); + // } + // + // Ok(string_table) + // } } + +//impl Expression { +// fn into_crane( +// self, +// builder: &mut FunctionBuilder, +// local_variables: &HashMap, (Variable, ConstantType)>, +// global_variables: &HashMap, +// ) -> Result<(entities::Value, ConstantType), BackendError> { +// match self { +// Expression::Atomic(x) => x.into_crane(builder, local_variables, global_variables), +// +// Expression::Cast(_, target_type, expr) => { +// let (val, val_type) = +// expr.into_crane(builder, local_variables, global_variables)?; +// +// match (val_type, &target_type) { +// } +// } +// +// Expression::Primitive(_, _, prim, mut vals) => { +// } +// } +// } +//} +// +//// Just to avoid duplication, this just leverages the `From` trait implementation +//// for `ValueOrRef` to compile this via the `Expression` logic, above. +//impl ValueOrRef { +// fn into_crane( +// self, +// builder: &mut FunctionBuilder, +// local_variables: &HashMap, (Variable, ConstantType)>, +// global_variables: &HashMap, +// ) -> Result<(entities::Value, ConstantType), BackendError> { +// match self { +// // Values are pretty straightforward to compile, mostly because we only +// // have one type of variable, and it's an integer type. +// ValueOrRef::Value(_, _, val) => match val { +// Value::I8(_, v) => { +// Ok((builder.ins().iconst(types::I8, v as i64), ConstantType::I8)) +// } +// Value::I16(_, v) => Ok(( +// builder.ins().iconst(types::I16, v as i64), +// ConstantType::I16, +// )), +// Value::I32(_, v) => Ok(( +// builder.ins().iconst(types::I32, v as i64), +// ConstantType::I32, +// )), +// Value::I64(_, v) => Ok((builder.ins().iconst(types::I64, v), ConstantType::I64)), +// Value::U8(_, v) => { +// Ok((builder.ins().iconst(types::I8, v as i64), ConstantType::U8)) +// } +// Value::U16(_, v) => Ok(( +// builder.ins().iconst(types::I16, v as i64), +// ConstantType::U16, +// )), +// Value::U32(_, v) => Ok(( +// builder.ins().iconst(types::I32, v as i64), +// ConstantType::U32, +// )), +// Value::U64(_, v) => Ok(( +// builder.ins().iconst(types::I64, v as i64), +// ConstantType::U64, +// )), +// }, +// +// ValueOrRef::Ref(_, _, name) => { +// // first we see if this is a local variable (which is nicer, from an +// // optimization point of view.) +// if let Some((local_var, etype)) = local_variables.get(&name) { +// return Ok((builder.use_var(*local_var), *etype)); +// } +// +// // then we check to see if this is a global reference, which requires us to +// // first lookup where the value is stored, and then load it. +// if let Some((global_var, etype)) = global_variables.get(name.as_ref()) { +// let cranelift_type = ir::Type::from(*etype); +// let val_ptr = builder.ins().symbol_value(cranelift_type, *global_var); +// return Ok(( +// builder +// .ins() +// .load(cranelift_type, MemFlags::new(), val_ptr, 0), +// *etype, +// )); +// } +// +// // this should never happen, because we should have made sure that there are +// // no unbound variables a long time before this. but still ... +// Err(BackendError::VariableLookupFailure(name)) +// } +// } +// } +//} +// diff --git a/src/compiler.rs b/src/compiler.rs index b94a200..22c8029 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -134,7 +134,7 @@ impl Compiler { // Finally, send all this to Cranelift for conversion into an object file. let mut backend = Backend::object_file(Triple::host())?; - backend.compile_function("gogogo", ir)?; + backend.compile_program("gogogo", ir)?; Ok(Some(backend.bytes()?)) } diff --git a/src/eval/primtype.rs b/src/eval/primtype.rs index de1e6eb..3c5818e 100644 --- a/src/eval/primtype.rs +++ b/src/eval/primtype.rs @@ -6,6 +6,7 @@ use std::{fmt::Display, str::FromStr}; #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub enum PrimitiveType { + Void, U8, U16, U32, @@ -19,6 +20,7 @@ pub enum PrimitiveType { impl Display for PrimitiveType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + PrimitiveType::Void => write!(f, "void"), PrimitiveType::I8 => write!(f, "i8"), PrimitiveType::I16 => write!(f, "i16"), PrimitiveType::I32 => write!(f, "i32"), @@ -100,6 +102,7 @@ impl PrimitiveType { /// Return true if this type can be safely cast into the target type. pub fn can_cast_to(&self, target: &PrimitiveType) -> bool { match self { + PrimitiveType::Void => matches!(target, PrimitiveType::Void), PrimitiveType::U8 => matches!( target, PrimitiveType::U8 @@ -175,16 +178,17 @@ impl PrimitiveType { } } - pub fn max_value(&self) -> u64 { + pub fn max_value(&self) -> Option { match self { - PrimitiveType::U8 => u8::MAX as u64, - PrimitiveType::U16 => u16::MAX as u64, - PrimitiveType::U32 => u32::MAX as u64, - PrimitiveType::U64 => u64::MAX, - PrimitiveType::I8 => i8::MAX as u64, - PrimitiveType::I16 => i16::MAX as u64, - PrimitiveType::I32 => i32::MAX as u64, - PrimitiveType::I64 => i64::MAX as u64, + PrimitiveType::Void => None, + PrimitiveType::U8 => Some(u8::MAX as u64), + PrimitiveType::U16 => Some(u16::MAX as u64), + PrimitiveType::U32 => Some(u32::MAX as u64), + PrimitiveType::U64 => Some(u64::MAX), + PrimitiveType::I8 => Some(i8::MAX as u64), + PrimitiveType::I16 => Some(i16::MAX as u64), + PrimitiveType::I32 => Some(i32::MAX as u64), + PrimitiveType::I64 => Some(i64::MAX as u64), } } } diff --git a/src/ir/ast.rs b/src/ir/ast.rs index c63f5db..728a4d0 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -9,36 +9,58 @@ use proptest::{ prelude::Arbitrary, strategy::{BoxedStrategy, Strategy}, }; -use std::{fmt, str::FromStr}; +use std::{fmt, str::FromStr, sync::atomic::AtomicUsize}; /// We're going to represent variables as interned strings. /// /// These should be fast enough for comparison that it's OK, since it's going to end up /// being pretty much the pointer to the string. -type Variable = ArcIntern; +pub type Variable = ArcIntern; + +/// Generate a new symbol that is guaranteed to be different from every other symbol +/// currently known. +/// +/// This function will use the provided string as a base name for the symbol, but +/// extend it with numbers and characters to make it unique. While technically you +/// could roll-over these symbols, you probably don't need to worry about it. +pub fn gensym(base: &str) -> Variable { + static COUNTER: AtomicUsize = AtomicUsize::new(0); + + ArcIntern::new(format!( + "{}<{}>", + base, + COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + )) +} /// 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. +/// A program consists of a series of statements and functions. The statements should +/// be executed in order. The functions currently may not reference any variables +/// at the top level, so their order only matters in relation to each other (functions +/// may not be referenced before they are defined). /// /// `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. +/// +/// The type variable is, somewhat confusingly, the current definition of a type within +/// the IR. Since the makeup of this structure may change over the life of the compiler, +/// it's easiest to just make it an argument. #[derive(Debug)] -pub struct Program { +pub struct Program { // For now, a program is just a vector of statements. In the future, we'll probably // extend this to include a bunch of other information, but for now: just a list. - pub(crate) items: Vec, + pub(crate) items: Vec>, } -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Program +impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b Program where A: 'a, D: ?Sized + DocAllocator<'a, A>, + &'b Type: Pretty<'a, D, A>, { fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { let mut result = allocator.nil(); @@ -56,17 +78,18 @@ where } } -impl Arbitrary for Program { +impl Arbitrary for Program { type Parameters = crate::syntax::arbitrary::GenerationEnvironment; type Strategy = BoxedStrategy; fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { - crate::syntax::Program::arbitrary_with(args) - .prop_map(|x| { - x.type_infer() - .expect("arbitrary_with should generate type-correct programs") - }) - .boxed() + unimplemented!() + //crate::syntax::Program::arbitrary_with(args) + // .prop_map(|x| { + // x.type_infer() + // .expect("arbitrary_with should generate type-correct programs") + // }) + // .boxed() } } @@ -76,84 +99,35 @@ impl Arbitrary for Program { /// will likely be added in the future, but for now: just statements /// and functions #[derive(Debug)] -pub enum TopLevel { - Statement(Statement), - Function(Variable, Vec, Vec, Expression), +pub enum TopLevel { + Statement(Expression), + Function(Variable, Vec<(Variable, Type)>, Type, Expression), } -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b TopLevel +impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b TopLevel where A: 'a, D: ?Sized + DocAllocator<'a, A>, + &'b Type: Pretty<'a, D, A>, { fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { match self { - TopLevel::Function(name, args, stmts, expr) => { - let base = allocator - .text("function") - .append(allocator.space()) - .append(allocator.text(name.as_ref().to_string())) - .append(allocator.space()) - .append( - pretty_comma_separated( - allocator, - &args.iter().map(PrettySymbol::from).collect(), - ) - .parens(), - ) - .append(allocator.space()); - - let mut body = allocator.nil(); - for stmt in stmts { - body = body - .append(stmt.pretty(allocator)) - .append(allocator.text(";")) - .append(allocator.hardline()); - } - body = body.append(expr.pretty(allocator)); - body = body.append(allocator.hardline()); - body = body.braces(); - base.append(body) - } - - TopLevel::Statement(stmt) => stmt.pretty(allocator), - } - } -} - -/// The representation of a statement in the language. -/// -/// 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, Type, Expression), - Print(Location, Type, Variable), -} - -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Statement -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { - match self { - Statement::Binding(_, var, _, expr) => allocator - .text(var.as_ref().to_string()) + TopLevel::Function(name, args, _, expr) => allocator + .text("function") .append(allocator.space()) - .append(allocator.text("=")) + .append(allocator.text(name.as_ref().to_string())) + .append(allocator.space()) + .append( + pretty_comma_separated( + allocator, + &args.iter().map(|(x, _)| PrettySymbol::from(x)).collect(), + ) + .parens(), + ) .append(allocator.space()) .append(expr.pretty(allocator)), - Statement::Print(_, _, var) => allocator - .text("print") - .append(allocator.space()) - .append(allocator.text(var.as_ref().to_string())), + + TopLevel::Statement(stmt) => stmt.pretty(allocator), } } } @@ -171,21 +145,27 @@ where /// that the referenced data will always either be a constant or a /// variable reference. #[derive(Debug)] -pub enum Expression { - Atomic(ValueOrRef), - Cast(Location, Type, ValueOrRef), - Primitive(Location, Type, Primitive, Vec), +pub enum Expression { + Atomic(ValueOrRef), + Cast(Location, Type, ValueOrRef), + Primitive(Location, Type, Primitive, Vec>), + Block(Location, Type, Vec>), + Print(Location, Variable), + Bind(Location, Variable, Type, Box>), } -impl Expression { +impl Expression { /// Return a reference to the type of the expression, as inferred or recently /// computed. - pub fn type_of(&self) -> &Type { + pub fn type_of(&self) -> Type { match self { - Expression::Atomic(ValueOrRef::Ref(_, t, _)) => t, - Expression::Atomic(ValueOrRef::Value(_, t, _)) => t, - Expression::Cast(_, t, _) => t, - Expression::Primitive(_, t, _, _) => t, + Expression::Atomic(ValueOrRef::Ref(_, t, _)) => t.clone(), + Expression::Atomic(ValueOrRef::Value(_, t, _)) => t.clone(), + Expression::Cast(_, t, _) => t.clone(), + Expression::Primitive(_, t, _, _) => t.clone(), + Expression::Block(_, t, _) => t.clone(), + Expression::Print(_, _) => Type::void(), + Expression::Bind(_, _, _, _) => Type::void(), } } @@ -196,14 +176,18 @@ impl Expression { Expression::Atomic(ValueOrRef::Value(l, _, _)) => l, Expression::Cast(l, _, _) => l, Expression::Primitive(l, _, _, _) => l, + Expression::Block(l, _, _) => l, + Expression::Print(l, _) => l, + Expression::Bind(l, _, _, _) => l, } } } -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Expression +impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b Expression where A: 'a, D: ?Sized + DocAllocator<'a, A>, + &'b Type: Pretty<'a, D, A>, { fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { match self { @@ -229,6 +213,35 @@ where Expression::Primitive(_, _, op, exprs) => { allocator.text(format!("!!{:?} with {} arguments!!", op, exprs.len())) } + Expression::Block(_, _, exprs) => match exprs.split_last() { + None => allocator.text("()"), + Some((last, &[])) => last.pretty(allocator), + Some((last, start)) => { + let mut result = allocator.text("{").append(allocator.hardline()); + + for stmt in start.iter() { + result = result + .append(stmt.pretty(allocator)) + .append(allocator.text(";")) + .append(allocator.hardline()); + } + + result + .append(last.pretty(allocator)) + .append(allocator.hardline()) + .append(allocator.text("}")) + } + }, + Expression::Print(_, var) => allocator + .text("print") + .append(allocator.space()) + .append(allocator.text(var.as_ref().to_string())), + Expression::Bind(_, var, _, expr) => allocator + .text(var.as_ref().to_string()) + .append(allocator.space()) + .append(allocator.text("=")) + .append(allocator.space()) + .append(expr.pretty(allocator)), } } } @@ -288,12 +301,12 @@ impl fmt::Display for Primitive { /// at this level. Instead, expressions that take arguments take one /// of these, which can only be a constant or a reference. #[derive(Clone, Debug)] -pub enum ValueOrRef { +pub enum ValueOrRef { Value(Location, Type, Value), Ref(Location, Type, ArcIntern), } -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b ValueOrRef +impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b ValueOrRef where A: 'a, D: ?Sized + DocAllocator<'a, A>, @@ -306,8 +319,8 @@ where } } -impl From for Expression { - fn from(value: ValueOrRef) -> Self { +impl From> for Expression { + fn from(value: ValueOrRef) -> Self { Expression::Atomic(value) } } @@ -434,3 +447,121 @@ impl fmt::Display for Type { } } } + +impl From for Type { + fn from(value: PrimitiveType) -> Self { + Type::Primitive(value) + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum TypeOrVar { + Primitive(PrimitiveType), + Variable(Location, ArcIntern), + Function(Vec, Box), +} + +impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b TypeOrVar +where + A: 'a, + D: ?Sized + DocAllocator<'a, A>, +{ + fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { + match self { + TypeOrVar::Primitive(x) => allocator.text(format!("{}", x)), + TypeOrVar::Variable(_, x) => allocator.text(x.to_string()), + TypeOrVar::Function(args, rettype) => { + pretty_comma_separated(allocator, &args.iter().collect()) + .parens() + .append(allocator.space()) + .append(allocator.text("->")) + .append(allocator.space()) + .append(rettype.pretty(allocator)) + } + } + } +} + +impl fmt::Display for TypeOrVar { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TypeOrVar::Primitive(x) => x.fmt(f), + TypeOrVar::Variable(_, v) => write!(f, "{}", v), + TypeOrVar::Function(args, rettype) => { + write!(f, " write!(f, "()")?, + Some((single, &[])) => { + write!(f, "({})", single)?; + } + Some((last_one, rest)) => { + write!(f, "(")?; + for arg in rest.iter() { + write!(f, "{}, ", arg); + } + write!(f, "{})", last_one)?; + } + } + write!(f, "->")?; + rettype.fmt(f)?; + write!(f, ">") + } + } + } +} + +impl TypeOrVar { + /// Generate a fresh type variable that is different from all previous type variables. + /// + /// This type variable is guaranteed to be unique across the process lifetime. Overuse + /// of this function could potentially cause overflow problems, but you're going to have + /// to try really hard (like, 2^64 times) to make that happen. The location bound to + /// this address will be purely manufactured; if you want to specify a location, use + /// [`TypeOrVar::new_located`]. + pub fn new() -> Self { + Self::new_located(Location::manufactured()) + } + + /// Generate a fresh type variable that is different from all previous type variables. + /// + /// This type variable is guaranteed to be unique across the process lifetime. Overuse + /// of this function could potentially cause overflow problems, but you're going to have + /// to try really hard (like, 2^64 times) to make that happen. + pub fn new_located(loc: Location) -> Self { + TypeOrVar::Variable(loc, gensym("t")) + } +} + +trait TypeWithVoid { + fn void() -> Self; +} + +impl TypeWithVoid for Type { + fn void() -> Self { + Type::Primitive(PrimitiveType::Void) + } +} + +impl TypeWithVoid for TypeOrVar { + fn void() -> Self { + TypeOrVar::Primitive(PrimitiveType::Void) + } +} + +//impl From for TypeOrVar { +// fn from(value: Type) -> Self { +// TypeOrVar::Type(value) +// } +//} + +impl> From for TypeOrVar { + fn from(value: T) -> Self { + match value.into() { + Type::Primitive(p) => TypeOrVar::Primitive(p), + Type::Function(args, ret) => TypeOrVar::Function( + args.into_iter().map(Into::into).collect(), + Box::new((*ret).into()), + ), + } + } +} diff --git a/src/ir/eval.rs b/src/ir/eval.rs index 93d56b0..af3e31e 100644 --- a/src/ir/eval.rs +++ b/src/ir/eval.rs @@ -1,8 +1,8 @@ use super::{Primitive, Type, ValueOrRef}; use crate::eval::{EvalEnvironment, EvalError, Value}; -use crate::ir::{Expression, Program, Statement, TopLevel}; +use crate::ir::{Expression, Program, TopLevel}; -impl Program { +impl Program { /// Evaluate the program, returning either an error or a string containing everything /// the program printed out. /// @@ -15,16 +15,7 @@ impl Program { match stmt { TopLevel::Function(_, _, _, _) => unimplemented!(), - TopLevel::Statement(Statement::Binding(_, name, _, value)) => { - let actual_value = value.eval(&env)?; - env = env.extend(name.clone(), actual_value); - } - - TopLevel::Statement(Statement::Print(_, _, name)) => { - let value = env.lookup(name.clone())?; - let line = format!("{} = {}\n", name, value); - stdout.push_str(&line); - } + TopLevel::Statement(_) => unimplemented!(), } } @@ -32,17 +23,21 @@ impl Program { } } -impl Expression { +impl Expression +where + T: Clone + Into, +{ fn eval(&self, env: &EvalEnvironment) -> Result { match self { Expression::Atomic(x) => x.eval(env), Expression::Cast(_, t, valref) => { let value = valref.eval(env)?; + let ty = t.clone().into(); - match t { + match ty { Type::Primitive(pt) => Ok(pt.safe_cast(&value)?), - Type::Function(_, _) => Err(EvalError::CastToFunction(t.to_string())), + Type::Function(_, _) => Err(EvalError::CastToFunction(ty.to_string())), } } @@ -61,11 +56,19 @@ impl Expression { Primitive::Divide => Ok(Value::calculate("/", arg_values)?), } } + + Expression::Block(_, _, _) => { + unimplemented!() + } + + Expression::Print(_, _) => unimplemented!(), + + Expression::Bind(_, _, _, _) => unimplemented!(), } } } -impl ValueOrRef { +impl ValueOrRef { fn eval(&self, env: &EvalEnvironment) -> Result { match self { ValueOrRef::Value(_, _, v) => match v { diff --git a/src/ir/strings.rs b/src/ir/strings.rs index dc05432..5d6d07f 100644 --- a/src/ir/strings.rs +++ b/src/ir/strings.rs @@ -1,8 +1,8 @@ -use super::ast::{Expression, Program, Statement, TopLevel}; +use super::ast::{Expression, Program, TopLevel}; use internment::ArcIntern; use std::collections::HashSet; -impl Program { +impl Program { /// Get the complete list of strings used within the program. /// /// For the purposes of this function, strings are the variables used in @@ -18,37 +18,18 @@ impl Program { } } -impl TopLevel { +impl TopLevel { fn register_strings(&self, string_set: &mut HashSet>) { match self { - TopLevel::Function(_, _, stmts, body) => { - for stmt in stmts.iter() { - stmt.register_strings(string_set); - } - body.register_strings(string_set); - } + TopLevel::Function(_, _, _, body) => body.register_strings(string_set), TopLevel::Statement(stmt) => stmt.register_strings(string_set), } } } -impl Statement { - fn register_strings(&self, string_set: &mut HashSet>) { - match self { - Statement::Binding(_, name, _, expr) => { - string_set.insert(name.clone()); - expr.register_strings(string_set); - } - - Statement::Print(_, _, name) => { - string_set.insert(name.clone()); - } - } - } -} - -impl Expression { +impl Expression { fn register_strings(&self, _string_set: &mut HashSet>) { // nothing has a string in here, at the moment + unimplemented!() } } diff --git a/src/repl.rs b/src/repl.rs index 1600a0f..ea4803e 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,4 +1,5 @@ use crate::backend::{Backend, BackendError}; +use crate::eval::PrimitiveType; use crate::syntax::{ConstantType, Location, ParserError, Statement, TopLevel}; use crate::type_infer::TypeInferenceResult; use crate::util::scoped_map::ScopedMap; @@ -130,10 +131,6 @@ impl REPL { let syntax = TopLevel::parse(entry, source)?; let program = match syntax { - TopLevel::Function(_, _, _) => { - unimplemented!() - } - TopLevel::Statement(Statement::Binding(loc, name, expr)) => { // if this is a variable binding, and we've never defined this variable before, // we should tell cranelift about it. this is optimistic; if we fail to compile, @@ -152,9 +149,7 @@ impl REPL { } } - TopLevel::Statement(nonbinding) => crate::syntax::Program { - items: vec![TopLevel::Statement(nonbinding)], - }, + x => crate::syntax::Program { items: vec![x] }, }; let (mut errors, mut warnings) = @@ -197,8 +192,9 @@ impl REPL { for message in warnings.drain(..).map(Into::into) { self.emit_diagnostic(message)?; } + let name = format!("line{}", line_no); - let function_id = self.jitter.compile_function(&name, result)?; + let function_id = self.jitter.compile_program(&name, result)?; self.jitter.module.finalize_definitions()?; let compiled_bytes = self.jitter.bytes(function_id); let compiled_function = diff --git a/src/type_infer.rs b/src/type_infer.rs index 08cd2da..d47c7f4 100644 --- a/src/type_infer.rs +++ b/src/type_infer.rs @@ -10,7 +10,6 @@ //! all the constraints we've generated. If that's successful, in the final phase, we //! do the final conversion to the IR AST, filling in any type information we've learned //! along the way. -mod ast; mod convert; mod finalize; mod solve; @@ -32,9 +31,8 @@ impl syntax::Program { /// /// You really should have made sure that this program was validated before running /// this method, otherwise you may experience panics during operation. - pub fn type_infer(self) -> TypeInferenceResult { - let mut constraint_db = vec![]; - let program = convert_program(self, &mut constraint_db); + pub fn type_infer(self) -> TypeInferenceResult> { + let (program, constraint_db) = convert_program(self); let inference_result = solve_constraints(constraint_db); inference_result.map(|resolutions| finalize_program(program, &resolutions)) diff --git a/src/type_infer/ast.rs b/src/type_infer/ast.rs deleted file mode 100644 index 21eb2fc..0000000 --- a/src/type_infer/ast.rs +++ /dev/null @@ -1,408 +0,0 @@ -pub use crate::ir::ast::Primitive; -/// This is largely a copy of `ir/ast`, with a couple of extensions that we're going -/// to want to use while we're doing type inference, but don't want to keep around -/// afterwards. These are: -/// -/// * A notion of a type variable -/// * An unknown numeric constant form -/// -use crate::{ - eval::PrimitiveType, - syntax::{self, ConstantType, Location}, - util::pretty::{pretty_comma_separated, PrettySymbol}, -}; -use internment::ArcIntern; -use pretty::{DocAllocator, Pretty}; -use std::fmt; -use std::sync::atomic::AtomicUsize; - -/// We're going to represent variables as interned strings. -/// -/// These should be fast enough for comparison that it's OK, since it's going to end up -/// being pretty much the pointer to the string. -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 - // extend this to include a bunch of other information, but for now: just a list. - pub(crate) items: Vec, -} - -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Program -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { - let mut result = allocator.nil(); - - for stmt in self.items.iter() { - // there's probably a better way to do this, rather than constantly - // adding to the end, but this works. - result = result - .append(stmt.pretty(allocator)) - .append(allocator.text(";")) - .append(allocator.hardline()); - } - - result - } -} - -/// A thing that can sit at the top level of a file. -/// -/// For the moment, these are statements and functions. Other things -/// will likely be added in the future, but for now: just statements -/// and functions -#[derive(Debug)] -pub enum TopLevel { - Statement(Statement), - Function(Variable, Vec, Vec, Expression), -} - -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b TopLevel -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { - match self { - TopLevel::Function(name, args, stmts, expr) => { - let base = allocator - .text("function") - .append(allocator.space()) - .append(allocator.text(name.as_ref().to_string())) - .append(allocator.space()) - .append( - pretty_comma_separated( - allocator, - &args.iter().map(PrettySymbol::from).collect(), - ) - .parens(), - ) - .append(allocator.space()); - - let mut body = allocator.nil(); - for stmt in stmts { - body = body - .append(stmt.pretty(allocator)) - .append(allocator.text(";")) - .append(allocator.hardline()); - } - body = body.append(expr.pretty(allocator)); - body = body.append(allocator.hardline()); - body = body.braces(); - base.append(body) - } - TopLevel::Statement(stmt) => stmt.pretty(allocator), - } - } -} - -/// The representation of a statement in the language. -/// -/// 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, Type, Expression), - Print(Location, Type, Variable), -} - -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Statement -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { - match self { - Statement::Binding(_, var, _, expr) => allocator - .text(var.as_ref().to_string()) - .append(allocator.space()) - .append(allocator.text("=")) - .append(allocator.space()) - .append(expr.pretty(allocator)), - Statement::Print(_, _, var) => allocator - .text("print") - .append(allocator.space()) - .append(allocator.text(var.as_ref().to_string())), - } - } -} - -/// 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, PartialEq)] -pub enum Expression { - Atomic(ValueOrRef), - Cast(Location, Type, ValueOrRef), - Primitive(Location, Type, Primitive, Vec), -} - -impl Expression { - /// Return a reference to the type of the expression, as inferred or recently - /// computed. - pub fn type_of(&self) -> &Type { - match self { - Expression::Atomic(ValueOrRef::Ref(_, t, _)) => t, - Expression::Atomic(ValueOrRef::Value(_, t, _)) => t, - Expression::Cast(_, t, _) => t, - Expression::Primitive(_, t, _, _) => t, - } - } - - /// Return a reference to the location associated with the expression. - pub fn location(&self) -> &Location { - match self { - Expression::Atomic(ValueOrRef::Ref(l, _, _)) => l, - Expression::Atomic(ValueOrRef::Value(l, _, _)) => l, - Expression::Cast(l, _, _) => l, - Expression::Primitive(l, _, _, _) => l, - } - } -} - -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Expression -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { - match self { - Expression::Atomic(x) => x.pretty(allocator), - Expression::Cast(_, t, e) => allocator - .text("<") - .append(t.pretty(allocator)) - .append(allocator.text(">")) - .append(e.pretty(allocator)), - Expression::Primitive(_, _, op, exprs) if exprs.len() == 1 => { - op.pretty(allocator).append(exprs[0].pretty(allocator)) - } - Expression::Primitive(_, _, op, exprs) if exprs.len() == 2 => { - let left = exprs[0].pretty(allocator); - let right = exprs[1].pretty(allocator); - - left.append(allocator.space()) - .append(op.pretty(allocator)) - .append(allocator.space()) - .append(right) - .parens() - } - Expression::Primitive(_, _, op, exprs) => { - allocator.text(format!("!!{:?} with {} arguments!!", op, exprs.len())) - } - } - } -} - -/// 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(Clone, Debug, PartialEq)] -pub enum ValueOrRef { - Value(Location, Type, Value), - Ref(Location, Type, ArcIntern), -} - -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b ValueOrRef -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { - match self { - ValueOrRef::Value(_, _, v) => v.pretty(allocator), - ValueOrRef::Ref(_, _, v) => allocator.text(v.as_ref().to_string()), - } - } -} - -impl From for Expression { - fn from(value: ValueOrRef) -> Self { - Expression::Atomic(value) - } -} - -/// A constant in the IR. -/// -/// The optional argument in numeric types 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. -#[derive(Clone, Debug, PartialEq)] -pub enum Value { - Unknown(Option, u64), - I8(Option, i8), - I16(Option, i16), - I32(Option, i32), - I64(Option, i64), - U8(Option, u8), - U16(Option, u16), - U32(Option, u32), - U64(Option, u64), -} - -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Value -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { - let pretty_internal = |opt_base: &Option, x, t| { - syntax::Value::Number(*opt_base, Some(t), x).pretty(allocator) - }; - - let pretty_internal_signed = |opt_base, x: i64, t| { - let base = pretty_internal(opt_base, x.unsigned_abs(), t); - - allocator.text("-").append(base) - }; - - match self { - Value::Unknown(opt_base, value) => { - pretty_internal_signed(opt_base, *value as i64, ConstantType::U64) - } - Value::I8(opt_base, value) => { - pretty_internal_signed(opt_base, *value as i64, ConstantType::I8) - } - Value::I16(opt_base, value) => { - pretty_internal_signed(opt_base, *value as i64, ConstantType::I16) - } - Value::I32(opt_base, value) => { - pretty_internal_signed(opt_base, *value as i64, ConstantType::I32) - } - Value::I64(opt_base, value) => { - pretty_internal_signed(opt_base, *value, ConstantType::I64) - } - Value::U8(opt_base, value) => { - pretty_internal(opt_base, *value as u64, ConstantType::U8) - } - Value::U16(opt_base, value) => { - pretty_internal(opt_base, *value as u64, ConstantType::U16) - } - Value::U32(opt_base, value) => { - pretty_internal(opt_base, *value as u64, ConstantType::U32) - } - Value::U64(opt_base, value) => pretty_internal(opt_base, *value, ConstantType::U64), - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum Type { - Variable(Location, ArcIntern), - Primitive(PrimitiveType), - Function(Vec, Box), -} - -impl Type { - pub fn is_concrete(&self) -> bool { - !matches!(self, Type::Variable(_, _)) - } -} - -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Type -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { - match self { - Type::Variable(_, x) => allocator.text(x.to_string()), - Type::Primitive(pt) => allocator.text(format!("{}", pt)), - Type::Function(args, rettype) => { - pretty_comma_separated(allocator, &args.iter().collect()) - .parens() - .append(allocator.space()) - .append(allocator.text("->")) - .append(allocator.space()) - .append(rettype.pretty(allocator)) - } - } - } -} - -impl fmt::Display for Type { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Type::Variable(_, x) => write!(f, "{}", x), - Type::Primitive(pt) => pt.fmt(f), - Type::Function(args, ret) => { - write!(f, "(")?; - let mut argiter = args.iter().peekable(); - while let Some(arg) = argiter.next() { - arg.fmt(f)?; - if argiter.peek().is_some() { - write!(f, ",")?; - } - } - write!(f, "->")?; - ret.fmt(f) - } - } - } -} - -/// Generate a fresh new name based on the given name. -/// -/// The new name is guaranteed to be unique across the entirety of the -/// execution. This is achieved by using characters in the variable name -/// that would not be valid input, and by including a counter that is -/// incremented on every invocation. -pub fn gensym(name: &str) -> ArcIntern { - static COUNTER: AtomicUsize = AtomicUsize::new(0); - - let new_name = format!( - "<{}:{}>", - name, - COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst) - ); - ArcIntern::new(new_name) -} - -/// Generate a fresh new type; this will be a unique new type variable. -/// -/// The new name is guaranteed to be unique across the entirety of the -/// execution. This is achieved by using characters in the variable name -/// that would not be valid input, and by including a counter that is -/// incremented on every invocation. -pub fn gentype() -> Type { - static COUNTER: AtomicUsize = AtomicUsize::new(0); - - let name = ArcIntern::new(format!( - "t<{}>", - COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst) - )); - - Type::Variable(Location::manufactured(), name) -} diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index e6dc161..d67f3d8 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -1,10 +1,9 @@ -use super::ast as ir; -use super::ast::Type; use crate::eval::PrimitiveType; +use crate::ir; use crate::syntax::{self, ConstantType}; use crate::type_infer::solve::Constraint; +use crate::util::scoped_map::ScopedMap; use internment::ArcIntern; -use std::collections::HashMap; use std::str::FromStr; /// This function takes a syntactic program and converts it into the IR version of the @@ -16,22 +15,22 @@ use std::str::FromStr; /// function can panic. pub fn convert_program( mut program: syntax::Program, - constraint_db: &mut Vec, -) -> ir::Program { +) -> (ir::Program, Vec) { + let mut constraint_db = Vec::new(); let mut items = Vec::new(); - let mut renames = HashMap::new(); - let mut bindings = HashMap::new(); + let mut renames = ScopedMap::new(); + let mut bindings = ScopedMap::new(); for item in program.items.drain(..) { - items.append(&mut convert_top_level( + items.push(convert_top_level( item, - constraint_db, + &mut constraint_db, &mut renames, &mut bindings, )); } - ir::Program { items } + (ir::Program { items }, constraint_db) } /// This function takes a top-level item and converts it into the IR version of the @@ -40,9 +39,9 @@ pub fn convert_program( pub fn convert_top_level( top_level: syntax::TopLevel, constraint_db: &mut Vec, - renames: &mut HashMap, ArcIntern>, - bindings: &mut HashMap, Type>, -) -> Vec { + renames: &mut ScopedMap, ArcIntern>, + bindings: &mut ScopedMap, ir::TypeOrVar>, +) -> ir::TopLevel { match top_level { syntax::TopLevel::Function(name, args, expr) => { // First, let us figure out what we're going to name this function. If the user @@ -59,9 +58,9 @@ pub fn convert_top_level( // Now we manufacture types for the inputs and outputs, and then a type for the // function itself. We're not going to make any claims on these types, yet; they're // all just unknown type variables we need to work out. - let argtypes: Vec = args.iter().map(|_| ir::gentype()).collect(); - let rettype = ir::gentype(); - let funtype = Type::Function(argtypes.clone(), Box::new(rettype.clone())); + let argtypes: Vec = args.iter().map(|_| ir::TypeOrVar::new()).collect(); + let rettype = ir::TypeOrVar::new(); + let funtype = ir::TypeOrVar::Function(argtypes.clone(), Box::new(rettype.clone())); // Now let's bind these types into the environment. First, we bind our function // namae to the function type we just generated. @@ -71,20 +70,20 @@ pub fn convert_top_level( let iargs: Vec> = args.iter().map(|x| ArcIntern::new(x.to_string())).collect(); assert_eq!(argtypes.len(), iargs.len()); + let mut function_args = vec![]; for (arg_name, arg_type) in iargs.iter().zip(argtypes) { bindings.insert(arg_name.clone(), arg_type.clone()); + function_args.push((arg_name.clone(), arg_type)); } - let (stmts, expr, ty) = convert_expression(expr, constraint_db, renames, bindings); - constraint_db.push(Constraint::Equivalent(expr.location().clone(), rettype, ty)); + let (expr, ty) = convert_expression(expr, constraint_db, renames, bindings); + constraint_db.push(Constraint::Equivalent(expr.location().clone(), rettype.clone(), ty)); - vec![ir::TopLevel::Function(funname, iargs, stmts, expr)] + ir::TopLevel::Function(funname, function_args, rettype, expr) } + syntax::TopLevel::Statement(stmt) => { - convert_statement(stmt, constraint_db, renames, bindings) - .drain(..) - .map(ir::TopLevel::Statement) - .collect() + ir::TopLevel::Statement(convert_statement(stmt, constraint_db, renames, bindings)) } } } @@ -103,9 +102,9 @@ pub fn convert_top_level( fn convert_statement( statement: syntax::Statement, constraint_db: &mut Vec, - renames: &mut HashMap, ArcIntern>, - bindings: &mut HashMap, Type>, -) -> Vec { + renames: &mut ScopedMap, ArcIntern>, + bindings: &mut ScopedMap, ir::TypeOrVar>, +) -> ir::Expression { match statement { syntax::Statement::Print(loc, name) => { let iname = ArcIntern::new(name.to_string()); @@ -120,17 +119,14 @@ fn convert_statement( constraint_db.push(Constraint::Printable(loc.clone(), varty.clone())); - vec![ir::Statement::Print(loc, varty, iname)] + ir::Expression::Print(loc, final_name) } syntax::Statement::Binding(loc, name, expr) => { - let (mut prereqs, expr, ty) = - convert_expression(expr, constraint_db, renames, bindings); + let (expr, ty) = convert_expression(expr, constraint_db, renames, bindings); let final_name = finalize_name(bindings, renames, name); - bindings.insert(final_name.clone(), ty.clone()); - prereqs.push(ir::Statement::Binding(loc, final_name, ty, expr)); - prereqs + ir::Expression::Bind(loc, final_name, ty, Box::new(expr)) } } } @@ -149,16 +145,18 @@ fn convert_statement( fn convert_expression( expression: syntax::Expression, constraint_db: &mut Vec, - renames: &HashMap, ArcIntern>, - bindings: &mut HashMap, Type>, -) -> (Vec, ir::Expression, Type) { + renames: &ScopedMap, ArcIntern>, + bindings: &mut ScopedMap, ir::TypeOrVar>, +) -> (ir::Expression, ir::TypeOrVar) { match expression { + // converting values is mostly tedious, because there's so many cases + // involved syntax::Expression::Value(loc, val) => match val { syntax::Value::Number(base, mctype, value) => { let (newval, newtype) = match mctype { None => { - let newtype = ir::gentype(); - let newval = ir::Value::Unknown(base, value); + let newtype = ir::TypeOrVar::new(); + let newval = ir::Value::U64(base, value); constraint_db.push(Constraint::ConstantNumericType( loc.clone(), @@ -168,35 +166,35 @@ fn convert_expression( } Some(ConstantType::U8) => ( ir::Value::U8(base, value as u8), - ir::Type::Primitive(PrimitiveType::U8), + ir::TypeOrVar::Primitive(PrimitiveType::U8), ), Some(ConstantType::U16) => ( ir::Value::U16(base, value as u16), - ir::Type::Primitive(PrimitiveType::U16), + ir::TypeOrVar::Primitive(PrimitiveType::U16), ), Some(ConstantType::U32) => ( ir::Value::U32(base, value as u32), - ir::Type::Primitive(PrimitiveType::U32), + ir::TypeOrVar::Primitive(PrimitiveType::U32), ), Some(ConstantType::U64) => ( ir::Value::U64(base, value), - ir::Type::Primitive(PrimitiveType::U64), + ir::TypeOrVar::Primitive(PrimitiveType::U64), ), Some(ConstantType::I8) => ( ir::Value::I8(base, value as i8), - ir::Type::Primitive(PrimitiveType::I8), + ir::TypeOrVar::Primitive(PrimitiveType::I8), ), Some(ConstantType::I16) => ( ir::Value::I16(base, value as i16), - ir::Type::Primitive(PrimitiveType::I16), + ir::TypeOrVar::Primitive(PrimitiveType::I16), ), Some(ConstantType::I32) => ( ir::Value::I32(base, value as i32), - ir::Type::Primitive(PrimitiveType::I32), + ir::TypeOrVar::Primitive(PrimitiveType::I32), ), Some(ConstantType::I64) => ( ir::Value::I64(base, value as i64), - ir::Type::Primitive(PrimitiveType::I64), + ir::TypeOrVar::Primitive(PrimitiveType::I64), ), }; @@ -206,7 +204,6 @@ fn convert_expression( value, )); ( - vec![], ir::Expression::Atomic(ir::ValueOrRef::Value(loc, newtype.clone(), newval)), newtype, ) @@ -223,35 +220,37 @@ fn convert_expression( let refexp = ir::Expression::Atomic(ir::ValueOrRef::Ref(loc, rtype.clone(), final_name)); - (vec![], refexp, rtype) + (refexp, rtype) } syntax::Expression::Cast(loc, target, expr) => { - let (mut stmts, nexpr, etype) = - convert_expression(*expr, constraint_db, renames, bindings); - let val_or_ref = simplify_expr(nexpr, &mut stmts); - let target_prim_type = PrimitiveType::from_str(&target).expect("valid type for cast"); - let target_type = Type::Primitive(target_prim_type); + let (nexpr, etype) = convert_expression(*expr, constraint_db, renames, bindings); + let (prereqs, val_or_ref) = simplify_expr(nexpr); + let target_type: ir::TypeOrVar = PrimitiveType::from_str(&target) + .expect("valid type for cast") + .into(); let res = ir::Expression::Cast(loc.clone(), target_type.clone(), val_or_ref); constraint_db.push(Constraint::CanCastTo(loc, etype, target_type.clone())); - (stmts, res, target_type) + (finalize_expression(prereqs, res), target_type) } syntax::Expression::Primitive(loc, fun, mut args) => { let primop = ir::Primitive::from_str(&fun).expect("valid primitive"); - let mut stmts = vec![]; + let mut prereqs = vec![]; let mut nargs = vec![]; let mut atypes = vec![]; - let ret_type = ir::gentype(); + let ret_type = ir::TypeOrVar::new(); for arg in args.drain(..) { - let (mut astmts, aexp, atype) = - convert_expression(arg, constraint_db, renames, bindings); + let (aexp, atype) = convert_expression(arg, constraint_db, renames, bindings); + let (aprereqs, asimple) = simplify_expr(aexp); - stmts.append(&mut astmts); - nargs.push(simplify_expr(aexp, &mut stmts)); + if let Some(prereq) = aprereqs { + prereqs.push(prereq); + } + nargs.push(asimple); atypes.push(atype); } @@ -262,33 +261,56 @@ fn convert_expression( ret_type.clone(), )); - ( - stmts, - ir::Expression::Primitive(loc, ret_type.clone(), primop, nargs), - ret_type, - ) + let last_call = ir::Expression::Primitive(loc.clone(), ret_type.clone(), primop, nargs); + + if prereqs.is_empty() { + (last_call, ret_type) + } else { + prereqs.push(last_call); + (ir::Expression::Block(loc, ret_type.clone(), prereqs), ret_type) + } } } } -fn simplify_expr(expr: ir::Expression, stmts: &mut Vec) -> ir::ValueOrRef { +fn simplify_expr( + expr: ir::Expression, +) -> ( + Option>, + ir::ValueOrRef, +) { match expr { - ir::Expression::Atomic(v_or_ref) => v_or_ref, + ir::Expression::Atomic(v_or_ref) => (None, v_or_ref), expr => { let etype = expr.type_of().clone(); let loc = expr.location().clone(); let nname = ir::gensym("g"); - let nbinding = ir::Statement::Binding(loc.clone(), nname.clone(), etype.clone(), expr); + let nbinding = + ir::Expression::Bind(loc.clone(), nname.clone(), etype.clone(), Box::new(expr)); - stmts.push(nbinding); - ir::ValueOrRef::Ref(loc, etype, nname) + (Some(nbinding), ir::ValueOrRef::Ref(loc, etype, nname)) } } } +fn finalize_expression( + prereq: Option>, + actual: ir::Expression, +) -> ir::Expression { + if let Some(prereq) = prereq { + ir::Expression::Block( + prereq.location().clone(), + actual.type_of().clone(), + vec![prereq, actual], + ) + } else { + actual + } +} + fn finalize_name( - bindings: &HashMap, Type>, - renames: &mut HashMap, ArcIntern>, + bindings: &ScopedMap, ir::TypeOrVar>, + renames: &mut ScopedMap, ArcIntern>, name: syntax::Name, ) -> ArcIntern { if bindings.contains_key(&ArcIntern::new(name.name.clone())) { @@ -302,139 +324,139 @@ fn finalize_name( #[cfg(test)] mod tests { - use super::*; - use crate::syntax::Location; - - fn one() -> syntax::Expression { - syntax::Expression::Value( - Location::manufactured(), - syntax::Value::Number(None, None, 1), - ) - } - - fn vec_contains bool>(x: &[T], f: F) -> bool { - for x in x.iter() { - if f(x) { - return true; - } - } - false - } - - fn infer_expression( - x: syntax::Expression, - ) -> (ir::Expression, Vec, Vec, Type) { - let mut constraints = Vec::new(); - let renames = HashMap::new(); - let mut bindings = HashMap::new(); - let (stmts, expr, ty) = convert_expression(x, &mut constraints, &renames, &mut bindings); - (expr, stmts, constraints, ty) - } - - fn infer_top_level(x: syntax::TopLevel) -> (Vec, Vec) { - let mut constraints = Vec::new(); - let mut renames = HashMap::new(); - let mut bindings = HashMap::new(); - let res = convert_top_level(x, &mut constraints, &mut renames, &mut bindings); - (res, constraints) - } - - #[test] - fn constant_one() { - let (expr, stmts, constraints, ty) = infer_expression(one()); - assert!(stmts.is_empty()); - assert!(matches!( - expr, - ir::Expression::Atomic(ir::ValueOrRef::Value(_, _, ir::Value::Unknown(None, 1))) - )); - assert!(vec_contains(&constraints, |x| matches!( - x, - Constraint::FitsInNumType(_, _, 1) - ))); - assert!(vec_contains( - &constraints, - |x| matches!(x, Constraint::ConstantNumericType(_, t) if t == &ty) - )); - } - - #[test] - fn one_plus_one() { - let opo = syntax::Expression::Primitive( - Location::manufactured(), - "+".to_string(), - vec![one(), one()], - ); - let (expr, stmts, constraints, ty) = infer_expression(opo); - assert!(stmts.is_empty()); - assert!(matches!(expr, ir::Expression::Primitive(_, t, ir::Primitive::Plus, _) if t == ty)); - assert!(vec_contains(&constraints, |x| matches!( - x, - Constraint::FitsInNumType(_, _, 1) - ))); - assert!(vec_contains( - &constraints, - |x| matches!(x, Constraint::ConstantNumericType(_, t) if t != &ty) - )); - assert!(vec_contains( - &constraints, - |x| matches!(x, Constraint::ProperPrimitiveArgs(_, ir::Primitive::Plus, args, ret) if args.len() == 2 && ret == &ty) - )); - } - - #[test] - fn one_plus_one_plus_one() { - let stmt = syntax::TopLevel::parse(1, "x = 1 + 1 + 1;").expect("basic parse"); - let (stmts, constraints) = infer_top_level(stmt); - assert_eq!(stmts.len(), 2); - let ir::TopLevel::Statement(ir::Statement::Binding( - _args, - name1, - temp_ty1, - ir::Expression::Primitive(_, primty1, ir::Primitive::Plus, primargs1), - )) = stmts.get(0).expect("item two") - else { - panic!("Failed to match first statement"); - }; - let ir::TopLevel::Statement(ir::Statement::Binding( - _args, - name2, - temp_ty2, - ir::Expression::Primitive(_, primty2, ir::Primitive::Plus, primargs2), - )) = stmts.get(1).expect("item two") - else { - panic!("Failed to match second statement"); - }; - let &[ir::ValueOrRef::Value(_, ref left1ty, _), ir::ValueOrRef::Value(_, ref right1ty, _)] = - &primargs1[..] - else { - panic!("Failed to match first arguments"); - }; - let &[ir::ValueOrRef::Ref(_, _, ref left2name), ir::ValueOrRef::Value(_, ref right2ty, _)] = - &primargs2[..] - else { - panic!("Failed to match first arguments"); - }; - assert_ne!(name1, name2); - assert_ne!(temp_ty1, temp_ty2); - assert_ne!(primty1, primty2); - assert_eq!(name1, left2name); - assert!(vec_contains( - &constraints, - |x| matches!(x, Constraint::ConstantNumericType(_, t) if t == left1ty) - )); - assert!(vec_contains( - &constraints, - |x| matches!(x, Constraint::ConstantNumericType(_, t) if t == right1ty) - )); - assert!(vec_contains( - &constraints, - |x| matches!(x, Constraint::ConstantNumericType(_, t) if t == right2ty) - )); - for (i, s) in stmts.iter().enumerate() { - println!("{}: {:?}", i, s); - } - for (i, c) in constraints.iter().enumerate() { - println!("{}: {:?}", i, c); - } - } + // use super::*; + // use crate::syntax::Location; + // + // fn one() -> syntax::Expression { + // syntax::Expression::Value( + // Location::manufactured(), + // syntax::Value::Number(None, None, 1), + // ) + // } + // + // fn vec_contains bool>(x: &[T], f: F) -> bool { + // for x in x.iter() { + // if f(x) { + // return true; + // } + // } + // false + // } + // + // fn infer_expression( + // x: syntax::Expression, + // ) -> (ir::Expression, Vec, Vec, Type) { + // let mut constraints = Vec::new(); + // let renames = HashMap::new(); + // let mut bindings = HashMap::new(); + // let (stmts, expr, ty) = convert_expression(x, &mut constraints, &renames, &mut bindings); + // (expr, stmts, constraints, ty) + // } + // + // fn infer_top_level(x: syntax::TopLevel) -> (Vec, Vec) { + // let mut constraints = Vec::new(); + // let mut renames = HashMap::new(); + // let mut bindings = HashMap::new(); + // let res = convert_top_level(x, &mut constraints, &mut renames, &mut bindings); + // (res, constraints) + // } + // + // #[test] + // fn constant_one() { + // let (expr, stmts, constraints, ty) = infer_expression(one()); + // assert!(stmts.is_empty()); + // assert!(matches!( + // expr, + // ir::Expression::Atomic(ir::ValueOrRef::Value(_, _, ir::Value::Unknown(None, 1))) + // )); + // assert!(vec_contains(&constraints, |x| matches!( + // x, + // Constraint::FitsInNumType(_, _, 1) + // ))); + // assert!(vec_contains( + // &constraints, + // |x| matches!(x, Constraint::ConstantNumericType(_, t) if t == &ty) + // )); + // } + // + // #[test] + // fn one_plus_one() { + // let opo = syntax::Expression::Primitive( + // Location::manufactured(), + // "+".to_string(), + // vec![one(), one()], + // ); + // let (expr, stmts, constraints, ty) = infer_expression(opo); + // assert!(stmts.is_empty()); + // assert!(matches!(expr, ir::Expression::Primitive(_, t, ir::Primitive::Plus, _) if t == ty)); + // assert!(vec_contains(&constraints, |x| matches!( + // x, + // Constraint::FitsInNumType(_, _, 1) + // ))); + // assert!(vec_contains( + // &constraints, + // |x| matches!(x, Constraint::ConstantNumericType(_, t) if t != &ty) + // )); + // assert!(vec_contains( + // &constraints, + // |x| matches!(x, Constraint::ProperPrimitiveArgs(_, ir::Primitive::Plus, args, ret) if args.len() == 2 && ret == &ty) + // )); + // } + // + // #[test] + // fn one_plus_one_plus_one() { + // let stmt = syntax::TopLevel::parse(1, "x = 1 + 1 + 1;").expect("basic parse"); + // let (stmts, constraints) = infer_top_level(stmt); + // assert_eq!(stmts.len(), 2); + // let ir::TopLevel::Statement(ir::Statement::Binding( + // _args, + // name1, + // temp_ty1, + // ir::Expression::Primitive(_, primty1, ir::Primitive::Plus, primargs1), + // )) = stmts.get(0).expect("item two") + // else { + // panic!("Failed to match first statement"); + // }; + // let ir::TopLevel::Statement(ir::Statement::Binding( + // _args, + // name2, + // temp_ty2, + // ir::Expression::Primitive(_, primty2, ir::Primitive::Plus, primargs2), + // )) = stmts.get(1).expect("item two") + // else { + // panic!("Failed to match second statement"); + // }; + // let &[ir::ValueOrRef::Value(_, ref left1ty, _), ir::ValueOrRef::Value(_, ref right1ty, _)] = + // &primargs1[..] + // else { + // panic!("Failed to match first arguments"); + // }; + // let &[ir::ValueOrRef::Ref(_, _, ref left2name), ir::ValueOrRef::Value(_, ref right2ty, _)] = + // &primargs2[..] + // else { + // panic!("Failed to match first arguments"); + // }; + // assert_ne!(name1, name2); + // assert_ne!(temp_ty1, temp_ty2); + // assert_ne!(primty1, primty2); + // assert_eq!(name1, left2name); + // assert!(vec_contains( + // &constraints, + // |x| matches!(x, Constraint::ConstantNumericType(_, t) if t == left1ty) + // )); + // assert!(vec_contains( + // &constraints, + // |x| matches!(x, Constraint::ConstantNumericType(_, t) if t == right1ty) + // )); + // assert!(vec_contains( + // &constraints, + // |x| matches!(x, Constraint::ConstantNumericType(_, t) if t == right2ty) + // )); + // for (i, s) in stmts.iter().enumerate() { + // println!("{}: {:?}", i, s); + // } + // for (i, c) in constraints.iter().enumerate() { + // println!("{}: {:?}", i, c); + // } + // } } diff --git a/src/type_infer/finalize.rs b/src/type_infer/finalize.rs index 763574c..577f4d8 100644 --- a/src/type_infer/finalize.rs +++ b/src/type_infer/finalize.rs @@ -1,11 +1,12 @@ -use super::{ast as input, solve::TypeResolutions}; -use crate::{eval::PrimitiveType, ir as output}; +use super::solve::TypeResolutions; +use crate::eval::PrimitiveType; +use crate::ir::{Expression, Program, TopLevel, Type, TypeOrVar, Value, ValueOrRef}; pub fn finalize_program( - mut program: input::Program, + mut program: Program, resolutions: &TypeResolutions, -) -> output::Program { - output::Program { +) -> Program { + Program { items: program .items .drain(..) @@ -14,53 +15,36 @@ pub fn finalize_program( } } -fn finalize_top_level(item: input::TopLevel, resolutions: &TypeResolutions) -> output::TopLevel { +fn finalize_top_level(item: TopLevel, resolutions: &TypeResolutions) -> TopLevel { match item { - input::TopLevel::Function(name, args, mut body, expr) => output::TopLevel::Function( - name, - args, - body.drain(..) - .map(|x| finalize_statement(x, resolutions)) - .collect(), - finalize_expression(expr, resolutions), - ), - input::TopLevel::Statement(stmt) => { - output::TopLevel::Statement(finalize_statement(stmt, resolutions)) - } - } -} - -fn finalize_statement( - statement: input::Statement, - resolutions: &TypeResolutions, -) -> output::Statement { - match statement { - input::Statement::Binding(loc, var, ty, expr) => output::Statement::Binding( - loc, - var, - finalize_type(ty, resolutions), - finalize_expression(expr, resolutions), - ), - input::Statement::Print(loc, ty, var) => { - output::Statement::Print(loc, finalize_type(ty, resolutions), var) + TopLevel::Function(name, args, rettype, expr) => { + TopLevel::Function( + name, + args.into_iter().map(|(name, t)| (name, finalize_type(t, resolutions))).collect(), + finalize_type(rettype, resolutions), + finalize_expression(expr, resolutions) + ) } + TopLevel::Statement(expr) => TopLevel::Statement(finalize_expression(expr, resolutions)), } } fn finalize_expression( - expression: input::Expression, + expression: Expression, resolutions: &TypeResolutions, -) -> output::Expression { +) -> Expression { match expression { - input::Expression::Atomic(val_or_ref) => { - output::Expression::Atomic(finalize_val_or_ref(val_or_ref, resolutions)) + Expression::Atomic(val_or_ref) => { + Expression::Atomic(finalize_val_or_ref(val_or_ref, resolutions)) } - input::Expression::Cast(loc, target, val_or_ref) => output::Expression::Cast( + + Expression::Cast(loc, target, val_or_ref) => Expression::Cast( loc, finalize_type(target, resolutions), finalize_val_or_ref(val_or_ref, resolutions), ), - input::Expression::Primitive(loc, ty, prim, mut args) => output::Expression::Primitive( + + Expression::Primitive(loc, ty, prim, mut args) => Expression::Primitive( loc, finalize_type(ty, resolutions), prim, @@ -68,17 +52,42 @@ fn finalize_expression( .map(|x| finalize_val_or_ref(x, resolutions)) .collect(), ), + + Expression::Block(loc, ty, mut exprs) => { + let mut final_exprs = Vec::with_capacity(exprs.len()); + + for expr in exprs { + let newexpr = finalize_expression(expr, resolutions); + + if let Expression::Block(_, _, mut subexprs) = newexpr { + final_exprs.append(&mut subexprs); + } else { + final_exprs.push(newexpr); + } + } + + Expression::Block(loc, finalize_type(ty, resolutions), final_exprs) + } + + Expression::Print(loc, var) => Expression::Print(loc, var), + + Expression::Bind(loc, var, ty, subexp) => Expression::Bind( + loc, + var, + finalize_type(ty, resolutions), + Box::new(finalize_expression(*subexp, resolutions)), + ), } } -fn finalize_type(ty: input::Type, resolutions: &TypeResolutions) -> output::Type { +fn finalize_type(ty: TypeOrVar, resolutions: &TypeResolutions) -> Type { match ty { - input::Type::Primitive(x) => output::Type::Primitive(x), - input::Type::Variable(_, tvar) => match resolutions.get(&tvar) { + TypeOrVar::Primitive(x) => Type::Primitive(x), + TypeOrVar::Variable(_, tvar) => match resolutions.get(&tvar) { None => panic!("Did not resolve type for type variable {}", tvar), - Some(pt) => output::Type::Primitive(*pt), + Some(pt) => Type::Primitive(*pt), }, - input::Type::Function(mut args, ret) => output::Type::Function( + TypeOrVar::Function(mut args, ret) => Type::Function( args.drain(..) .map(|x| finalize_type(x, resolutions)) .collect(), @@ -88,123 +97,82 @@ fn finalize_type(ty: input::Type, resolutions: &TypeResolutions) -> output::Type } fn finalize_val_or_ref( - valref: input::ValueOrRef, + valref: ValueOrRef, resolutions: &TypeResolutions, -) -> output::ValueOrRef { +) -> ValueOrRef { match valref { - input::ValueOrRef::Ref(loc, ty, var) => { - output::ValueOrRef::Ref(loc, finalize_type(ty, resolutions), var) - } - input::ValueOrRef::Value(loc, ty, val) => { + ValueOrRef::Ref(loc, ty, var) => ValueOrRef::Ref(loc, finalize_type(ty, resolutions), var), + ValueOrRef::Value(loc, ty, val) => { let new_type = finalize_type(ty, resolutions); match val { - input::Value::Unknown(base, value) => match new_type { - output::Type::Function(_, _) => { + // U64 is essentially "unknown" for us, so we use the inferred type + Value::U64(base, value) => match new_type { + Type::Function(_, _) => { panic!("Somehow inferred that a constant was a function") } - output::Type::Primitive(PrimitiveType::U8) => output::ValueOrRef::Value( - loc, - new_type, - output::Value::U8(base, value as u8), - ), - output::Type::Primitive(PrimitiveType::U16) => output::ValueOrRef::Value( - loc, - new_type, - output::Value::U16(base, value as u16), - ), - output::Type::Primitive(PrimitiveType::U32) => output::ValueOrRef::Value( - loc, - new_type, - output::Value::U32(base, value as u32), - ), - output::Type::Primitive(PrimitiveType::U64) => { - output::ValueOrRef::Value(loc, new_type, output::Value::U64(base, value)) + Type::Primitive(PrimitiveType::Void) => { + panic!("Somehow inferred that a constant was void") + } + Type::Primitive(PrimitiveType::U8) => { + ValueOrRef::Value(loc, new_type, Value::U8(base, value as u8)) + } + Type::Primitive(PrimitiveType::U16) => { + ValueOrRef::Value(loc, new_type, Value::U16(base, value as u16)) + } + Type::Primitive(PrimitiveType::U32) => { + ValueOrRef::Value(loc, new_type, Value::U32(base, value as u32)) + } + Type::Primitive(PrimitiveType::U64) => { + ValueOrRef::Value(loc, new_type, Value::U64(base, value)) + } + Type::Primitive(PrimitiveType::I8) => { + ValueOrRef::Value(loc, new_type, Value::I8(base, value as i8)) + } + Type::Primitive(PrimitiveType::I16) => { + ValueOrRef::Value(loc, new_type, Value::I16(base, value as i16)) + } + Type::Primitive(PrimitiveType::I32) => { + ValueOrRef::Value(loc, new_type, Value::I32(base, value as i32)) + } + Type::Primitive(PrimitiveType::I64) => { + ValueOrRef::Value(loc, new_type, Value::I64(base, value as i64)) } - output::Type::Primitive(PrimitiveType::I8) => output::ValueOrRef::Value( - loc, - new_type, - output::Value::I8(base, value as i8), - ), - output::Type::Primitive(PrimitiveType::I16) => output::ValueOrRef::Value( - loc, - new_type, - output::Value::I16(base, value as i16), - ), - output::Type::Primitive(PrimitiveType::I32) => output::ValueOrRef::Value( - loc, - new_type, - output::Value::I32(base, value as i32), - ), - output::Type::Primitive(PrimitiveType::I64) => output::ValueOrRef::Value( - loc, - new_type, - output::Value::I64(base, value as i64), - ), }, - input::Value::U8(base, value) => { - assert!(matches!( - new_type, - output::Type::Primitive(PrimitiveType::U8) - )); - output::ValueOrRef::Value(loc, new_type, output::Value::U8(base, value)) + Value::U8(base, value) => { + assert!(matches!(new_type, Type::Primitive(PrimitiveType::U8))); + ValueOrRef::Value(loc, new_type, Value::U8(base, value)) } - input::Value::U16(base, value) => { - assert!(matches!( - new_type, - output::Type::Primitive(PrimitiveType::U16) - )); - output::ValueOrRef::Value(loc, new_type, output::Value::U16(base, value)) + Value::U16(base, value) => { + assert!(matches!(new_type, Type::Primitive(PrimitiveType::U16))); + ValueOrRef::Value(loc, new_type, Value::U16(base, value)) } - input::Value::U32(base, value) => { - assert!(matches!( - new_type, - output::Type::Primitive(PrimitiveType::U32) - )); - output::ValueOrRef::Value(loc, new_type, output::Value::U32(base, value)) + Value::U32(base, value) => { + assert!(matches!(new_type, Type::Primitive(PrimitiveType::U32))); + ValueOrRef::Value(loc, new_type, Value::U32(base, value)) } - input::Value::U64(base, value) => { - assert!(matches!( - new_type, - output::Type::Primitive(PrimitiveType::U64) - )); - output::ValueOrRef::Value(loc, new_type, output::Value::U64(base, value)) + Value::I8(base, value) => { + assert!(matches!(new_type, Type::Primitive(PrimitiveType::I8))); + ValueOrRef::Value(loc, new_type, Value::I8(base, value)) } - input::Value::I8(base, value) => { - assert!(matches!( - new_type, - output::Type::Primitive(PrimitiveType::I8) - )); - output::ValueOrRef::Value(loc, new_type, output::Value::I8(base, value)) + Value::I16(base, value) => { + assert!(matches!(new_type, Type::Primitive(PrimitiveType::I16))); + ValueOrRef::Value(loc, new_type, Value::I16(base, value)) } - input::Value::I16(base, value) => { - assert!(matches!( - new_type, - output::Type::Primitive(PrimitiveType::I16) - )); - output::ValueOrRef::Value(loc, new_type, output::Value::I16(base, value)) + Value::I32(base, value) => { + assert!(matches!(new_type, Type::Primitive(PrimitiveType::I32))); + ValueOrRef::Value(loc, new_type, Value::I32(base, value)) } - input::Value::I32(base, value) => { - assert!(matches!( - new_type, - output::Type::Primitive(PrimitiveType::I32) - )); - output::ValueOrRef::Value(loc, new_type, output::Value::I32(base, value)) - } - - input::Value::I64(base, value) => { - assert!(matches!( - new_type, - output::Type::Primitive(PrimitiveType::I64) - )); - output::ValueOrRef::Value(loc, new_type, output::Value::I64(base, value)) + Value::I64(base, value) => { + assert!(matches!(new_type, Type::Primitive(PrimitiveType::I64))); + ValueOrRef::Value(loc, new_type, Value::I64(base, value)) } } } diff --git a/src/type_infer/solve.rs b/src/type_infer/solve.rs index 76d072b..f3ba8c8 100644 --- a/src/type_infer/solve.rs +++ b/src/type_infer/solve.rs @@ -1,6 +1,6 @@ -use super::ast as ir; -use super::ast::Type; -use crate::{eval::PrimitiveType, syntax::Location}; +use crate::eval::PrimitiveType; +use crate::ir::{Primitive, TypeOrVar}; +use crate::syntax::Location; use codespan_reporting::diagnostic::Diagnostic; use internment::ArcIntern; use std::{collections::HashMap, fmt}; @@ -9,22 +9,22 @@ use std::{collections::HashMap, fmt}; #[derive(Debug)] pub enum Constraint { /// The given type must be printable using the `print` built-in - Printable(Location, Type), + Printable(Location, TypeOrVar), /// The provided numeric value fits in the given constant type - FitsInNumType(Location, Type, u64), + FitsInNumType(Location, TypeOrVar, u64), /// The given primitive has the proper arguments types associated with it - ProperPrimitiveArgs(Location, ir::Primitive, Vec, Type), + ProperPrimitiveArgs(Location, Primitive, Vec, TypeOrVar), /// The given type can be casted to the target type safely - CanCastTo(Location, Type, Type), + CanCastTo(Location, TypeOrVar, TypeOrVar), /// The given type must be some numeric type, but this is not a constant /// value, so don't try to default it if we can't figure it out - NumericType(Location, Type), + NumericType(Location, TypeOrVar), /// The given type is attached to a constant and must be some numeric type. /// If we can't figure it out, we should warn the user and then just use a /// default. - ConstantNumericType(Location, Type), + ConstantNumericType(Location, TypeOrVar), /// The two types should be equivalent - Equivalent(Location, Type, Type), + Equivalent(Location, TypeOrVar, TypeOrVar), } impl fmt::Display for Constraint { @@ -101,20 +101,22 @@ impl TypeInferenceResult { pub enum TypeInferenceError { /// The user provide a constant that is too large for its inferred type. ConstantTooLarge(Location, PrimitiveType, u64), + /// Somehow we're trying to use a non-number as a number + NotANumber(Location, PrimitiveType), /// The two types needed to be equivalent, but weren't. - NotEquivalent(Location, Type, Type), + NotEquivalent(Location, TypeOrVar, TypeOrVar), /// We cannot safely cast the first type to the second type. CannotSafelyCast(Location, PrimitiveType, PrimitiveType), /// The primitive invocation provided the wrong number of arguments. - WrongPrimitiveArity(Location, ir::Primitive, usize, usize, usize), + WrongPrimitiveArity(Location, Primitive, usize, usize, usize), /// We cannot cast between function types at the moment. - CannotCastBetweenFunctinoTypes(Location, Type, Type), + CannotCastBetweenFunctinoTypes(Location, TypeOrVar, TypeOrVar), /// We cannot cast from a function type to something else. - CannotCastFromFunctionType(Location, Type), + CannotCastFromFunctionType(Location, TypeOrVar), /// We cannot cast to a function type from something else. - CannotCastToFunctionType(Location, Type), + CannotCastToFunctionType(Location, TypeOrVar), /// We cannot turn a number into a function. - CannotMakeNumberAFunction(Location, Type, Option), + CannotMakeNumberAFunction(Location, TypeOrVar, Option), /// We had a constraint we just couldn't solve. CouldNotSolve(Constraint), } @@ -127,9 +129,15 @@ impl From for Diagnostic { .with_message(format!( "Type {} has a max value of {}, which is smaller than {}", primty, - primty.max_value(), + primty.max_value().expect("constant type has max value"), value )), + TypeInferenceError::NotANumber(loc, primty) => loc + .labelled_error("not a numeric type") + .with_message(format!( + "For some reason, we're trying to use {} as a numeric type", + primty, + )), TypeInferenceError::NotEquivalent(loc, ty1, ty2) => loc .labelled_error("type inference error") .with_message(format!("Expected type {}, received type {}", ty1, ty2)), @@ -214,7 +222,7 @@ impl From for Diagnostic { /// These are fine, probably, but could indicate some behavior the user might not /// expect, and so they might want to do something about them. pub enum TypeInferenceWarning { - DefaultedTo(Location, Type), + DefaultedTo(Location, TypeOrVar), } impl From for Diagnostic { @@ -270,7 +278,11 @@ pub fn solve_constraints( // Case #1a: We have two primitive types. If they're equal, we've discharged this // constraint! We can just continue. If they're not equal, add an error and then // see what else we come up with. - Constraint::Equivalent(loc, a @ Type::Primitive(_), b @ Type::Primitive(_)) => { + Constraint::Equivalent( + loc, + a @ TypeOrVar::Primitive(_), + b @ TypeOrVar::Primitive(_), + ) => { if a != b { errors.push(TypeInferenceError::NotEquivalent(loc, a, b)); } @@ -281,8 +293,16 @@ pub fn solve_constraints( // In this case, we'll check to see if we've resolved the variable, and check for // equivalence if we have. If we haven't, we'll set that variable to be primitive // type. - Constraint::Equivalent(loc, Type::Primitive(t), Type::Variable(_, name)) - | Constraint::Equivalent(loc, Type::Variable(_, name), Type::Primitive(t)) => { + Constraint::Equivalent( + loc, + TypeOrVar::Primitive(t), + TypeOrVar::Variable(_, name), + ) + | Constraint::Equivalent( + loc, + TypeOrVar::Variable(_, name), + TypeOrVar::Primitive(t), + ) => { match resolutions.get(&name) { None => { resolutions.insert(name, t); @@ -290,8 +310,8 @@ pub fn solve_constraints( Some(t2) if &t == t2 => {} Some(t2) => errors.push(TypeInferenceError::NotEquivalent( loc, - Type::Primitive(t), - Type::Primitive(*t2), + TypeOrVar::Primitive(t), + TypeOrVar::Primitive(*t2), )), } changed_something = true; @@ -301,8 +321,8 @@ pub fn solve_constraints( // check, but now on their resolutions. Constraint::Equivalent( ref loc, - Type::Variable(_, ref name1), - Type::Variable(_, ref name2), + TypeOrVar::Variable(_, ref name1), + TypeOrVar::Variable(_, ref name2), ) => match (resolutions.get(name1), resolutions.get(name2)) { (None, None) => { constraint_db.push(constraint); @@ -321,8 +341,8 @@ pub fn solve_constraints( (Some(pt1), Some(pt2)) => { errors.push(TypeInferenceError::NotEquivalent( loc.clone(), - Type::Primitive(*pt1), - Type::Primitive(*pt2), + TypeOrVar::Primitive(*pt1), + TypeOrVar::Primitive(*pt2), )); changed_something = true; } @@ -339,8 +359,8 @@ pub fn solve_constraints( // function types. Constraint::Equivalent( loc, - ref a @ Type::Function(ref args1, ref ret1), - ref b @ Type::Function(ref args2, ref ret2), + ref a @ TypeOrVar::Function(ref args1, ref ret1), + ref b @ TypeOrVar::Function(ref args2, ref ret2), ) => { if args1.len() != args2.len() { errors.push(TypeInferenceError::NotEquivalent( @@ -377,27 +397,33 @@ pub fn solve_constraints( // Make sure that the provided number fits within the provided constant type. For the // moment, we're going to call an error here a failure, although this could be a // warning in the future. - Constraint::FitsInNumType(loc, Type::Primitive(ctype), val) => { - if ctype.max_value() < val { - errors.push(TypeInferenceError::ConstantTooLarge(loc, ctype, val)); - } + Constraint::FitsInNumType(loc, TypeOrVar::Primitive(ctype), val) => { + match ctype.max_value() { + None => { + errors.push(TypeInferenceError::NotANumber(loc, ctype)); + } + Some(max_value) if max_value < val => { + errors.push(TypeInferenceError::ConstantTooLarge(loc, ctype, val)); + } + Some(_) => {} + }; changed_something = true; } // If we have a non-constant type, then let's see if we can advance this to a constant // type - Constraint::FitsInNumType(loc, Type::Variable(vloc, var), val) => { + Constraint::FitsInNumType(loc, TypeOrVar::Variable(vloc, var), val) => { match resolutions.get(&var) { None => constraint_db.push(Constraint::FitsInNumType( loc, - Type::Variable(vloc, var), + TypeOrVar::Variable(vloc, var), val, )), Some(nt) => { constraint_db.push(Constraint::FitsInNumType( loc, - Type::Primitive(*nt), + TypeOrVar::Primitive(*nt), val, )); changed_something = true; @@ -406,7 +432,7 @@ pub fn solve_constraints( } // Function types definitely do not fit in numeric types - Constraint::FitsInNumType(loc, t @ Type::Function(_, _), val) => { + Constraint::FitsInNumType(loc, t @ TypeOrVar::Function(_, _), val) => { errors.push(TypeInferenceError::CannotMakeNumberAFunction( loc, t.clone(), @@ -416,17 +442,17 @@ pub fn solve_constraints( // If the left type in a "can cast to" check is a variable, let's see if we can advance // it into something more tangible - Constraint::CanCastTo(loc, Type::Variable(vloc, var), to_type) => { + Constraint::CanCastTo(loc, TypeOrVar::Variable(vloc, var), to_type) => { match resolutions.get(&var) { None => constraint_db.push(Constraint::CanCastTo( loc, - Type::Variable(vloc, var), + TypeOrVar::Variable(vloc, var), to_type, )), Some(nt) => { constraint_db.push(Constraint::CanCastTo( loc, - Type::Primitive(*nt), + TypeOrVar::Primitive(*nt), to_type, )); changed_something = true; @@ -435,18 +461,18 @@ pub fn solve_constraints( } // If the right type in a "can cast to" check is a variable, same deal - Constraint::CanCastTo(loc, from_type, Type::Variable(vloc, var)) => { + Constraint::CanCastTo(loc, from_type, TypeOrVar::Variable(vloc, var)) => { match resolutions.get(&var) { None => constraint_db.push(Constraint::CanCastTo( loc, from_type, - Type::Variable(vloc, var), + TypeOrVar::Variable(vloc, var), )), Some(nt) => { constraint_db.push(Constraint::CanCastTo( loc, from_type, - Type::Primitive(*nt), + TypeOrVar::Primitive(*nt), )); changed_something = true; } @@ -456,8 +482,8 @@ pub fn solve_constraints( // If both of them are types, then we can actually do the test. yay! Constraint::CanCastTo( loc, - Type::Primitive(from_type), - Type::Primitive(to_type), + TypeOrVar::Primitive(from_type), + TypeOrVar::Primitive(to_type), ) => { if !from_type.can_cast_to(&to_type) { errors.push(TypeInferenceError::CannotSafelyCast( @@ -471,8 +497,8 @@ pub fn solve_constraints( // are equivalent. Constraint::CanCastTo( loc, - t1 @ Type::Function(_, _), - t2 @ Type::Function(_, _), + t1 @ TypeOrVar::Function(_, _), + t2 @ TypeOrVar::Function(_, _), ) => { if t1 != t2 { errors.push(TypeInferenceError::CannotCastBetweenFunctinoTypes( @@ -484,7 +510,11 @@ pub fn solve_constraints( changed_something = true; } - Constraint::CanCastTo(loc, t @ Type::Function(_, _), Type::Primitive(_)) => { + Constraint::CanCastTo( + loc, + t @ TypeOrVar::Function(_, _), + TypeOrVar::Primitive(_), + ) => { errors.push(TypeInferenceError::CannotCastFromFunctionType( loc, t.clone(), @@ -492,19 +522,24 @@ pub fn solve_constraints( changed_something = true; } - Constraint::CanCastTo(loc, Type::Primitive(_), t @ Type::Function(_, _)) => { + Constraint::CanCastTo( + loc, + TypeOrVar::Primitive(_), + t @ TypeOrVar::Function(_, _), + ) => { errors.push(TypeInferenceError::CannotCastToFunctionType(loc, t.clone())); changed_something = true; } // As per usual, if we're trying to test if a type variable is numeric, first // we try to advance it to a primitive - Constraint::NumericType(loc, Type::Variable(vloc, var)) => { + Constraint::NumericType(loc, TypeOrVar::Variable(vloc, var)) => { match resolutions.get(&var) { None => constraint_db - .push(Constraint::NumericType(loc, Type::Variable(vloc, var))), + .push(Constraint::NumericType(loc, TypeOrVar::Variable(vloc, var))), Some(nt) => { - constraint_db.push(Constraint::NumericType(loc, Type::Primitive(*nt))); + constraint_db + .push(Constraint::NumericType(loc, TypeOrVar::Primitive(*nt))); changed_something = true; } } @@ -512,12 +547,12 @@ pub fn solve_constraints( // Of course, if we get to a primitive type, then it's true, because all of our // primitive types are numbers - Constraint::NumericType(_, Type::Primitive(_)) => { + Constraint::NumericType(_, TypeOrVar::Primitive(_)) => { changed_something = true; } // But functions are definitely not numbers - Constraint::NumericType(loc, t @ Type::Function(_, _)) => { + Constraint::NumericType(loc, t @ TypeOrVar::Function(_, _)) => { errors.push(TypeInferenceError::CannotMakeNumberAFunction( loc, t.clone(), @@ -528,15 +563,17 @@ pub fn solve_constraints( // As per usual, if we're trying to test if a type variable is numeric, first // we try to advance it to a primitive - Constraint::ConstantNumericType(loc, Type::Variable(vloc, var)) => { + Constraint::ConstantNumericType(loc, TypeOrVar::Variable(vloc, var)) => { match resolutions.get(&var) { None => constraint_db.push(Constraint::ConstantNumericType( loc, - Type::Variable(vloc, var), + TypeOrVar::Variable(vloc, var), )), Some(nt) => { - constraint_db - .push(Constraint::ConstantNumericType(loc, Type::Primitive(*nt))); + constraint_db.push(Constraint::ConstantNumericType( + loc, + TypeOrVar::Primitive(*nt), + )); changed_something = true; } } @@ -544,12 +581,12 @@ pub fn solve_constraints( // Of course, if we get to a primitive type, then it's true, because all of our // primitive types are numbers - Constraint::ConstantNumericType(_, Type::Primitive(_)) => { + Constraint::ConstantNumericType(_, TypeOrVar::Primitive(_)) => { changed_something = true; } // But functions are definitely not numbers - Constraint::ConstantNumericType(loc, t @ Type::Function(_, _)) => { + Constraint::ConstantNumericType(loc, t @ TypeOrVar::Function(_, _)) => { errors.push(TypeInferenceError::CannotMakeNumberAFunction( loc, t.clone(), @@ -565,9 +602,7 @@ pub fn solve_constraints( // find by discovering that the number of arguments provided doesn't make sense // given the primitive being used. Constraint::ProperPrimitiveArgs(loc, prim, mut args, ret) => match prim { - ir::Primitive::Plus | ir::Primitive::Times | ir::Primitive::Divide - if args.len() != 2 => - { + Primitive::Plus | Primitive::Times | Primitive::Divide if args.len() != 2 => { errors.push(TypeInferenceError::WrongPrimitiveArity( loc, prim, @@ -578,7 +613,7 @@ pub fn solve_constraints( changed_something = true; } - ir::Primitive::Plus | ir::Primitive::Times | ir::Primitive::Divide => { + Primitive::Plus | Primitive::Times | Primitive::Divide => { let right = args.pop().expect("2 > 0"); let left = args.pop().expect("2 > 1"); @@ -596,7 +631,7 @@ pub fn solve_constraints( changed_something = true; } - ir::Primitive::Minus if args.is_empty() || args.len() > 2 => { + Primitive::Minus if args.is_empty() || args.len() > 2 => { errors.push(TypeInferenceError::WrongPrimitiveArity( loc, prim, @@ -607,7 +642,7 @@ pub fn solve_constraints( changed_something = true; } - ir::Primitive::Minus if args.len() == 1 => { + Primitive::Minus if args.len() == 1 => { let arg = args.pop().expect("1 > 0"); constraint_db.push(Constraint::NumericType(loc.clone(), arg.clone())); constraint_db.push(Constraint::NumericType(loc.clone(), ret.clone())); @@ -615,7 +650,7 @@ pub fn solve_constraints( changed_something = true; } - ir::Primitive::Minus => { + Primitive::Minus => { let right = args.pop().expect("2 > 0"); let left = args.pop().expect("2 > 1"); @@ -648,12 +683,12 @@ pub fn solve_constraints( for constraint in local_constraints.drain(..) { match constraint { - Constraint::ConstantNumericType(loc, t @ Type::Variable(_, _)) => { - let resty = Type::Primitive(PrimitiveType::U64); + Constraint::ConstantNumericType(loc, t @ TypeOrVar::Variable(_, _)) => { + let resty = TypeOrVar::Primitive(PrimitiveType::U64); constraint_db.push(Constraint::Equivalent( loc.clone(), t, - Type::Primitive(PrimitiveType::U64), + TypeOrVar::Primitive(PrimitiveType::U64), )); warnings.push(TypeInferenceWarning::DefaultedTo(loc, resty)); changed_something = true; diff --git a/src/util/pretty.rs b/src/util/pretty.rs index e5c478a..2cfa55e 100644 --- a/src/util/pretty.rs +++ b/src/util/pretty.rs @@ -8,7 +8,9 @@ pub struct PrettySymbol { impl<'a> From<&'a ArcIntern> for PrettySymbol { fn from(value: &'a ArcIntern) -> Self { - PrettySymbol { name: value.clone() } + PrettySymbol { + name: value.clone(), + } } } -- 2.53.0 From 2c2268925a39e47784f9e0c2062738797fceb753 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sun, 3 Dec 2023 17:32:37 -0800 Subject: [PATCH 08/59] todo: arbitrary ir --- src/backend/eval.rs | 10 ++--- src/backend/into_crane.rs | 39 +++++-------------- src/eval.rs | 20 +++++----- src/eval/primop.rs | 48 ++++++++++++++++++----- src/eval/primtype.rs | 13 ++++--- src/eval/value.rs | 80 +++++++++++++++++++++++++------------- src/ir.rs | 1 + src/ir/arbitrary.rs | 21 ++++++++++ src/ir/ast.rs | 31 +++++---------- src/ir/eval.rs | 73 +++++++++++++++++++++++++--------- src/repl.rs | 1 - src/syntax/eval.rs | 61 +++++++++++++++++++---------- src/type_infer.rs | 13 +++++-- src/type_infer/convert.rs | 11 +++++- src/type_infer/finalize.rs | 18 ++++----- src/util/scoped_map.rs | 21 ++++++++++ 16 files changed, 298 insertions(+), 163 deletions(-) create mode 100644 src/ir/arbitrary.rs diff --git a/src/backend/eval.rs b/src/backend/eval.rs index 81129eb..de7a1fa 100644 --- a/src/backend/eval.rs +++ b/src/backend/eval.rs @@ -26,7 +26,7 @@ impl Backend { /// library do. So, if you're validating equivalence between them, you'll want to weed /// out examples that overflow/underflow before checking equivalence. (This is the behavior /// of the built-in test systems.) - pub fn eval(program: Program) -> Result { + pub fn eval(program: Program) -> Result>> { let mut jitter = Backend::jit(Some(String::new()))?; let mut function_map = HashMap::new(); let mut main_function_body = vec![]; @@ -80,7 +80,7 @@ impl Backend { /// library do. So, if you're validating equivalence between them, you'll want to weed /// out examples that overflow/underflow before checking equivalence. (This is the behavior /// of the built-in test systems.) - pub fn eval(program: Program) -> Result { + pub fn eval(program: Program) -> Result>> { //use pretty::{Arena, Pretty}; //let allocator = Arena::<()>::new(); //program.pretty(&allocator).render(80, &mut std::io::stdout())?; @@ -147,7 +147,7 @@ impl Backend { /// This function assumes that this compilation and linking should run without any /// output, so changes to the RTS should make 100% sure that they do not generate /// any compiler warnings. - fn link(object_file: &Path, executable_path: &Path) -> Result<(), EvalError> { + fn link(object_file: &Path, executable_path: &Path) -> Result<(), EvalError>> { use std::path::PathBuf; let output = std::process::Command::new("clang") @@ -179,7 +179,7 @@ proptest::proptest! { fn static_backend(program in Program::arbitrary_with(GenerationEnvironment::new(false))) { use crate::eval::PrimOpError; - let basic_result = program.eval(); + let basic_result = program.eval().map(|(_,x)| x); // windows `printf` is going to terminate lines with "\r\n", so we need to adjust // our test result here. @@ -219,7 +219,7 @@ proptest::proptest! { // .expect("rendering works"); - let basic_result = program.eval(); + let basic_result = program.eval().map(|(_,x)| x); if !matches!(basic_result, Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))) { let compiled_result = Backend::::eval(program); diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index 0f77339..d897f7c 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -1,5 +1,3 @@ -use std::collections::HashMap; - use crate::eval::PrimitiveType; use crate::ir::{Expression, Primitive, Program, TopLevel, Type, Value, ValueOrRef, Variable}; use crate::syntax::{ConstantType, Location}; @@ -16,14 +14,6 @@ use internment::ArcIntern; use crate::backend::error::BackendError; use crate::backend::Backend; -/// When we're compiling, we might need to reference some of the strings built into -/// the source code; to do so, we need a `GlobalValue`. Perhaps unexpectedly, given -/// the name, `GlobalValue`s are specific to a single function we're compiling, so -/// we end up computing this table for every function. -/// -/// This just a handy type alias to avoid a lot of confusion in the functions. -type StringTable = HashMap, GlobalValue>; - /// When we're talking about variables, it's handy to just have a table that points /// from a variable to "what to do if you want to reference this variable", which is /// agnostic about whether the variable is local, global, an argument, etc. Since @@ -36,7 +26,9 @@ struct ReferenceBuilder { impl ReferenceBuilder { fn refer_to(&self, builder: &mut FunctionBuilder) -> (entities::Value, ConstantType) { - let value = builder.ins().symbol_value(self.cranelift_type, self.local_data); + let value = builder + .ins() + .symbol_value(self.cranelift_type, self.local_data); (value, self.ir_type) } } @@ -83,7 +75,7 @@ impl Backend { for item in program.items { match item { TopLevel::Function(name, args, rettype, body) => { - self.compile_function(name.as_str(), &args, rettype, body); + self.compile_function(name.as_str(), &args, rettype, body)?; } TopLevel::Statement(stmt) => { @@ -139,18 +131,6 @@ impl Backend { let user_func_name = UserFuncName::user(0, func_id.as_u32()); ctx.func = Function::with_name_signature(user_func_name, basic_signature); - // In the future, we might want to see what runtime functions the function - // we were given uses, and then only include those functions that we care - // about. Presumably, we'd use some sort of lookup table like we do for - // strings. But for now, we only have one runtime function, and we're pretty - // sure we're always going to use it, so we just declare it (and reference - // it) directly. - let print_func_ref = self.runtime_functions.include_runtime_function( - "print", - &mut self.module, - &mut ctx.func, - )?; - // Let's start creating the variable table we'll use when we're dereferencing // them later. This table is a little interesting because instead of pointing // from data to data, we're going to point from data (the variable) to an @@ -166,7 +146,11 @@ impl Backend { let cranelift_type = ir::Type::from(*ty); variables.insert( name.clone(), - ReferenceBuilder { cranelift_type, local_data, ir_type: *ty }, + ReferenceBuilder { + cranelift_type, + local_data, + ir_type: *ty, + }, ); } @@ -176,9 +160,6 @@ impl Backend { // to win. variables.new_scope(); - // FIXME: Add arguments - let mut next_var_num = 1; - // Finally (!), we generate the function builder that we're going to use to // make this function! let mut fctx = FunctionBuilderContext::new(); @@ -326,7 +307,7 @@ impl Backend { for inner in exprs { // we can ignore all of these return values and such, because we // don't actually use them anywhere - self.compile_expression(inner, variables, builder); + self.compile_expression(inner, variables, builder)?; } // instead, we just return the last one self.compile_expression(last, variables, builder) diff --git a/src/eval.rs b/src/eval.rs index b45b4d6..f0fee04 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -33,13 +33,12 @@ //! because the implementation of some parts of these primitives is really //! awful to look at. //! -mod env; +//mod env; mod primop; mod primtype; mod value; use cranelift_module::ModuleError; -pub use env::{EvalEnvironment, LookupError}; pub use primop::PrimOpError; pub use primtype::PrimitiveType; pub use value::Value; @@ -56,11 +55,9 @@ use self::primtype::UnknownPrimType; /// of converting those errors to strings and then seeing if they're the /// same. #[derive(Debug, thiserror::Error)] -pub enum EvalError { +pub enum EvalError { #[error(transparent)] - Lookup(#[from] LookupError), - #[error(transparent)] - PrimOp(#[from] PrimOpError), + PrimOp(#[from] PrimOpError), #[error(transparent)] Backend(#[from] BackendError), #[error("IO error: {0}")] @@ -77,16 +74,17 @@ pub enum EvalError { CastToFunction(String), #[error(transparent)] UnknownPrimType(#[from] UnknownPrimType), + #[error("Variable lookup failed for {1} at {0:?}")] + LookupFailed(crate::syntax::Location, String), } -impl PartialEq for EvalError { - fn eq(&self, other: &Self) -> bool { +impl PartialEq> for EvalError { + fn eq(&self, other: &EvalError) -> bool { match self { - EvalError::Lookup(a) => match other { - EvalError::Lookup(b) => a == b, + EvalError::LookupFailed(a, b) => match other { + EvalError::LookupFailed(x, y) => a == x && b == y, _ => false, }, - EvalError::PrimOp(a) => match other { EvalError::PrimOp(b) => a == b, _ => false, diff --git a/src/eval/primop.rs b/src/eval/primop.rs index 41634a3..c4fcd25 100644 --- a/src/eval/primop.rs +++ b/src/eval/primop.rs @@ -4,19 +4,19 @@ use crate::eval::value::Value; use super::primtype::{UnknownPrimType, ValuePrimitiveTypeError}; /// Errors that can occur running primitive operations in the evaluators. -#[derive(Clone, Debug, PartialEq, thiserror::Error)] -pub enum PrimOpError { +#[derive(Clone, Debug, thiserror::Error)] +pub enum PrimOpError { #[error("Math error (underflow or overflow) computing {0} operator")] MathFailure(&'static str), /// This particular variant covers the case in which a primitive /// operator takes two arguments that are supposed to be the same, /// but they differ. (So, like, all the math operators.) #[error("Type mismatch ({1} vs {2}) computing {0} operator")] - TypeMismatch(String, Value, Value), + TypeMismatch(String, Value, Value), /// This variant covers when an operator must take a particular /// type, but the user has provided a different one. #[error("Bad type for operator {0}: {1}")] - BadTypeFor(String, Value), + BadTypeFor(String, Value), /// Probably obvious from the name, but just to be very clear: this /// happens when you pass three arguments to a two argument operator, /// etc. Technically that's a type error of some sort, but we split @@ -36,6 +36,29 @@ pub enum PrimOpError { ValuePrimitiveTypeError(#[from] ValuePrimitiveTypeError), } +impl PartialEq> for PrimOpError { + fn eq(&self, other: &PrimOpError) -> bool { + match (self, other) { + (PrimOpError::MathFailure(a), PrimOpError::MathFailure(b)) => a == b, + (PrimOpError::TypeMismatch(a, b, c), PrimOpError::TypeMismatch(x, y, z)) => { + a == x && b.strip() == y.strip() && c.strip() == z.strip() + } + (PrimOpError::BadTypeFor(a, b), PrimOpError::BadTypeFor(x, y)) => a == x && b.strip() == y.strip(), + (PrimOpError::BadArgCount(a, b), PrimOpError::BadArgCount(x, y)) => a == x && b == y, + (PrimOpError::UnknownPrimOp(a), PrimOpError::UnknownPrimOp(x)) => a == x, + ( + PrimOpError::UnsafeCast { from: a, to: b }, + PrimOpError::UnsafeCast { from: x, to: y }, + ) => a == x && b == y, + (PrimOpError::UnknownPrimType(a), PrimOpError::UnknownPrimType(x)) => a == x, + (PrimOpError::ValuePrimitiveTypeError(a), PrimOpError::ValuePrimitiveTypeError(x)) => { + a == x + } + _ => false, + } + } +} + // Implementing primitives in an interpreter like this is *super* tedious, // and the only way to make it even somewhat manageable is to use macros. // This particular macro works for binary operations, and assumes that @@ -59,8 +82,8 @@ macro_rules! run_op { }; } -impl Value { - fn unary_op(operation: &str, value: &Value) -> Result { +impl Value { + fn unary_op(operation: &str, value: &Value) -> Result, PrimOpError> { match operation { "-" => match value { Value::I8(x) => Ok(Value::I8(x.wrapping_neg())), @@ -73,7 +96,11 @@ impl Value { } } - fn binary_op(operation: &str, left: &Value, right: &Value) -> Result { + fn binary_op( + operation: &str, + left: &Value, + right: &Value, + ) -> Result, PrimOpError> { match left { Value::I8(x) => match right { Value::I8(y) => run_op!(operation, x, *y), @@ -139,7 +166,7 @@ impl Value { right.clone(), )), }, - Value::Function(_, _) => { + Value::Closure(_, _, _, _) | Value::Void => { Err(PrimOpError::BadTypeFor(operation.to_string(), left.clone())) } } @@ -153,7 +180,10 @@ impl Value { /// implementation catches and raises an error on overflow or underflow, so /// its worth being careful to make sure that your inputs won't cause either /// condition. - pub fn calculate(operation: &str, values: Vec) -> Result { + pub fn calculate( + operation: &str, + values: Vec>, + ) -> Result, PrimOpError> { match values.len() { 1 => Value::unary_op(operation, &values[0]), 2 => Value::binary_op(operation, &values[0], &values[1]), diff --git a/src/eval/primtype.rs b/src/eval/primtype.rs index 3c5818e..0876310 100644 --- a/src/eval/primtype.rs +++ b/src/eval/primtype.rs @@ -39,11 +39,12 @@ pub enum ValuePrimitiveTypeError { CannotConvertFunction(Option), } -impl<'a> TryFrom<&'a Value> for PrimitiveType { +impl<'a, IR> TryFrom<&'a Value> for PrimitiveType { type Error = ValuePrimitiveTypeError; - fn try_from(value: &'a Value) -> Result { + fn try_from(value: &'a Value) -> Result { match value { + Value::Void => Ok(PrimitiveType::Void), Value::I8(_) => Ok(PrimitiveType::I8), Value::I16(_) => Ok(PrimitiveType::I16), Value::I32(_) => Ok(PrimitiveType::I32), @@ -52,9 +53,9 @@ impl<'a> TryFrom<&'a Value> for PrimitiveType { Value::U16(_) => Ok(PrimitiveType::U16), Value::U32(_) => Ok(PrimitiveType::U32), Value::U64(_) => Ok(PrimitiveType::U64), - Value::Function(name, _) => { - Err(ValuePrimitiveTypeError::CannotConvertFunction(name.clone())) - } + Value::Closure(name, _, _, _) => Err(ValuePrimitiveTypeError::CannotConvertFunction( + name.as_ref().map(|x| (**x).clone()), + )), } } } @@ -147,7 +148,7 @@ impl PrimitiveType { /// type to the target type. (So, for example, "1i64" is a number that could /// work as a "u64", but since negative numbers wouldn't work, a cast from /// "1i64" to "u64" will fail.) - pub fn safe_cast(&self, source: &Value) -> Result { + pub fn safe_cast(&self, source: &Value) -> Result, PrimOpError> { match (self, source) { (PrimitiveType::U8, Value::U8(x)) => Ok(Value::U8(*x)), (PrimitiveType::U16, Value::U8(x)) => Ok(Value::U16(*x as u16)), diff --git a/src/eval/value.rs b/src/eval/value.rs index 3634a2f..26c020b 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -1,6 +1,6 @@ -use super::EvalError; +use crate::util::scoped_map::ScopedMap; +use internment::ArcIntern; use std::fmt; -use std::rc::Rc; /// Values in the interpreter. /// @@ -8,7 +8,8 @@ use std::rc::Rc; /// are almost entirely identical. However, it's nice to have them separated /// by type so that we don't mix them up. #[derive(Clone)] -pub enum Value { +pub enum Value { + Void, I8(i8), I16(i16), I32(i32), @@ -17,14 +18,44 @@ pub enum Value { U16(u16), U32(u32), U64(u64), - Function( - Option, - Rc) -> Result>, + Closure( + Option>, + ScopedMap, Value>, + Vec>, + IR, ), } -fn format_value(value: &Value, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl Value { + /// Given a Value associated with some expression type, just strip out + /// the expressions and replace them with unit. + /// + /// Doing this transformation will likely make this value useless for + /// computation, but is very useful in allowing equivalence checks. + pub fn strip(&self) -> Value<()> { + match self { + Value::Void => Value::Void, + Value::U8(x) => Value::U8(*x), + Value::U16(x) => Value::U16(*x), + Value::U32(x) => Value::U32(*x), + Value::U64(x) => Value::U64(*x), + Value::I8(x) => Value::I8(*x), + Value::I16(x) => Value::I16(*x), + Value::I32(x) => Value::I32(*x), + Value::I64(x) => Value::I64(*x), + Value::Closure(name, env, args, _) => { + let new_env = env + .clone() + .map_values(|x| x.strip()); + Value::Closure(name.clone(), new_env, args.clone(), ()) + } + } + } +} + +fn format_value(value: &Value, f: &mut fmt::Formatter<'_>) -> fmt::Result { match value { + Value::Void => write!(f, ""), Value::I8(x) => write!(f, "{}i8", x), Value::I16(x) => write!(f, "{}i16", x), Value::I32(x) => write!(f, "{}i32", x), @@ -33,26 +64,27 @@ fn format_value(value: &Value, f: &mut fmt::Formatter<'_>) -> fmt::Result { Value::U16(x) => write!(f, "{}u16", x), Value::U32(x) => write!(f, "{}u32", x), Value::U64(x) => write!(f, "{}u64", x), - Value::Function(Some(name), _) => write!(f, "", name), - Value::Function(None, _) => write!(f, ""), + Value::Closure(Some(name), _, _, _) => write!(f, "", name), + Value::Closure(None, _, _, _) => write!(f, ""), } } -impl fmt::Debug for Value { +impl fmt::Debug for Value { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { format_value(self, f) } } -impl fmt::Display for Value { +impl fmt::Display for Value { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { format_value(self, f) } } -impl PartialEq for Value { - fn eq(&self, other: &Self) -> bool { +impl PartialEq> for Value { + fn eq(&self, other: &Value) -> bool { match self { + Value::Void => matches!(other, Value::Void), Value::I8(x) => match other { Value::I8(y) => x == y, _ => false, @@ -85,58 +117,54 @@ impl PartialEq for Value { Value::U64(y) => x == y, _ => false, }, - Value::Function(Some(x), _) => match other { - Value::Function(Some(y), _) => x == y, - _ => false, - }, - Value::Function(None, _) => false, + Value::Closure(_, _, _, _) => false, } } } -impl From for Value { +impl From for Value { fn from(value: i8) -> Self { Value::I8(value) } } -impl From for Value { +impl From for Value { fn from(value: i16) -> Self { Value::I16(value) } } -impl From for Value { +impl From for Value { fn from(value: i32) -> Self { Value::I32(value) } } -impl From for Value { +impl From for Value { fn from(value: i64) -> Self { Value::I64(value) } } -impl From for Value { +impl From for Value { fn from(value: u8) -> Self { Value::U8(value) } } -impl From for Value { +impl From for Value { fn from(value: u16) -> Self { Value::U16(value) } } -impl From for Value { +impl From for Value { fn from(value: u32) -> Self { Value::U32(value) } } -impl From for Value { +impl From for Value { fn from(value: u64) -> Self { Value::U64(value) } diff --git a/src/ir.rs b/src/ir.rs index af38b02..bb4d0bd 100644 --- a/src/ir.rs +++ b/src/ir.rs @@ -12,6 +12,7 @@ //! validating syntax, and then figuring out how to turn it into Cranelift //! and object code. After that point, however, this will be the module to //! come to for analysis and optimization work. +mod arbitrary; pub mod ast; mod eval; mod strings; diff --git a/src/ir/arbitrary.rs b/src/ir/arbitrary.rs new file mode 100644 index 0000000..94f925a --- /dev/null +++ b/src/ir/arbitrary.rs @@ -0,0 +1,21 @@ +use crate::ir::{Program, TopLevel, Expression, ValueOrRef, Value, Type}; +use proptest::{ + prelude::Arbitrary, + strategy::{BoxedStrategy, Strategy}, +}; + +impl Arbitrary for Program { + type Parameters = crate::syntax::arbitrary::GenerationEnvironment; + type Strategy = BoxedStrategy; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + unimplemented!() + //crate::syntax::Program::arbitrary_with(args) + // .prop_map(|x| { + // x.type_infer() + // .expect("arbitrary_with should generate type-correct programs") + // }) + // .boxed() + } +} + diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 728a4d0..67c8b12 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -5,10 +5,6 @@ use crate::{ }; use internment::ArcIntern; use pretty::{BoxAllocator, DocAllocator, Pretty}; -use proptest::{ - prelude::Arbitrary, - strategy::{BoxedStrategy, Strategy}, -}; use std::{fmt, str::FromStr, sync::atomic::AtomicUsize}; /// We're going to represent variables as interned strings. @@ -78,21 +74,6 @@ where } } -impl Arbitrary for Program { - type Parameters = crate::syntax::arbitrary::GenerationEnvironment; - type Strategy = BoxedStrategy; - - fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { - unimplemented!() - //crate::syntax::Program::arbitrary_with(args) - // .prop_map(|x| { - // x.type_infer() - // .expect("arbitrary_with should generate type-correct programs") - // }) - // .boxed() - } -} - /// A thing that can sit at the top level of a file. /// /// For the moment, these are statements and functions. Other things @@ -144,7 +125,7 @@ where /// 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)] +#[derive(Clone, Debug)] pub enum Expression { Atomic(ValueOrRef), Cast(Location, Type, ValueOrRef), @@ -497,7 +478,7 @@ impl fmt::Display for TypeOrVar { Some((last_one, rest)) => { write!(f, "(")?; for arg in rest.iter() { - write!(f, "{}, ", arg); + write!(f, "{}, ", arg)?; } write!(f, "{})", last_one)?; } @@ -510,6 +491,12 @@ impl fmt::Display for TypeOrVar { } } +impl Default for TypeOrVar { + fn default() -> Self { + TypeOrVar::new() + } +} + impl TypeOrVar { /// Generate a fresh type variable that is different from all previous type variables. /// @@ -532,7 +519,7 @@ impl TypeOrVar { } } -trait TypeWithVoid { +pub trait TypeWithVoid { fn void() -> Self; } diff --git a/src/ir/eval.rs b/src/ir/eval.rs index af3e31e..dd6340d 100644 --- a/src/ir/eval.rs +++ b/src/ir/eval.rs @@ -1,33 +1,55 @@ use super::{Primitive, Type, ValueOrRef}; -use crate::eval::{EvalEnvironment, EvalError, Value}; -use crate::ir::{Expression, Program, TopLevel}; +use crate::eval::{EvalError, Value}; +use crate::ir::{Expression, Program, TopLevel, Variable}; +use crate::util::scoped_map::ScopedMap; -impl Program { - /// Evaluate the program, returning either an error or a string containing everything - /// the program printed out. +type IRValue = Value>; +type IREvalError = EvalError>; + +impl> Program { + /// Evaluate the program, returning either an error or the result of the final + /// statement and the complete contents of the console output. /// /// The print outs will be newline separated, with one print out per line. - pub fn eval(&self) -> Result { - let mut env = EvalEnvironment::empty(); + pub fn eval(&self) -> Result<(IRValue, String), IREvalError> { + let mut env: ScopedMap> = ScopedMap::new(); let mut stdout = String::new(); + let mut last_value = Value::Void; for stmt in self.items.iter() { match stmt { - TopLevel::Function(_, _, _, _) => unimplemented!(), + TopLevel::Function(name, args, _, body) => { + let closure = Value::Closure( + Some(name.clone()), + env.clone(), + args.iter().map(|(x, _)| x.clone()).collect(), + body.clone(), + ); - TopLevel::Statement(_) => unimplemented!(), + env.insert(name.clone(), closure.clone()); + + last_value = closure; + } + + TopLevel::Statement(expr) => { + last_value = expr.eval(&env, &mut stdout)?; + } } } - Ok(stdout) + Ok((last_value, stdout)) } } impl Expression where - T: Clone + Into, + T: Clone + Into, { - fn eval(&self, env: &EvalEnvironment) -> Result { + fn eval( + &self, + env: &ScopedMap>, + stdout: &mut String, + ) -> Result, IREvalError> { match self { Expression::Atomic(x) => x.eval(env), @@ -45,7 +67,7 @@ where let arg_values = args .iter() .map(|x| x.eval(env)) - .collect::, EvalError>>()?; + .collect::>, IREvalError>>()?; // and then finally we call `calculate` to run them. trust me, it's nice // to not have to deal with all the nonsense hidden under `calculate`. @@ -61,15 +83,25 @@ where unimplemented!() } - Expression::Print(_, _) => unimplemented!(), + Expression::Print(loc, n) => { + let value = env + .get(n) + .cloned() + .ok_or_else(|| EvalError::LookupFailed(loc.clone(), n.to_string()))?; + stdout.push_str(&format!("{} = {}\n", n, value)); + Ok(Value::Void) + } Expression::Bind(_, _, _, _) => unimplemented!(), } } } -impl ValueOrRef { - fn eval(&self, env: &EvalEnvironment) -> Result { +impl ValueOrRef { + fn eval( + &self, + env: &ScopedMap>, + ) -> Result, IREvalError> { match self { ValueOrRef::Value(_, _, v) => match v { super::Value::I8(_, v) => Ok(Value::I8(*v)), @@ -82,7 +114,10 @@ impl ValueOrRef { super::Value::U64(_, v) => Ok(Value::U64(*v)), }, - ValueOrRef::Ref(_, _, n) => Ok(env.lookup(n.clone())?), + ValueOrRef::Ref(loc, _, n) => env + .get(n) + .cloned() + .ok_or_else(|| EvalError::LookupFailed(loc.clone(), n.to_string())), } } } @@ -91,7 +126,7 @@ impl ValueOrRef { fn two_plus_three() { let input = crate::syntax::Program::parse(0, "x = 2 + 3; print x;").expect("parse works"); let ir = input.type_infer().expect("test should be type-valid"); - let output = ir.eval().expect("runs successfully"); + let (_, output) = ir.eval().expect("runs successfully"); assert_eq!("x = 5u64\n", &output); } @@ -100,6 +135,6 @@ fn lotsa_math() { let input = crate::syntax::Program::parse(0, "x = 2 + 3 * 10 / 5 - 1; print x;").expect("parse works"); let ir = input.type_infer().expect("test should be type-valid"); - let output = ir.eval().expect("runs successfully"); + let (_, output) = ir.eval().expect("runs successfully"); assert_eq!("x = 7u64\n", &output); } diff --git a/src/repl.rs b/src/repl.rs index ea4803e..7d90a50 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,5 +1,4 @@ use crate::backend::{Backend, BackendError}; -use crate::eval::PrimitiveType; use crate::syntax::{ConstantType, Location, ParserError, Statement, TopLevel}; use crate::type_infer::TypeInferenceResult; use crate::util::scoped_map::ScopedMap; diff --git a/src/syntax/eval.rs b/src/syntax/eval.rs index 928d182..8025d07 100644 --- a/src/syntax/eval.rs +++ b/src/syntax/eval.rs @@ -1,11 +1,12 @@ +use crate::eval::{EvalError, PrimitiveType, Value}; +use crate::syntax::{ConstantType, Expression, Name, Program, Statement, TopLevel}; +use crate::util::scoped_map::ScopedMap; use internment::ArcIntern; - -use crate::eval::{EvalEnvironment, EvalError, PrimitiveType, Value}; -use crate::syntax::{ConstantType, Expression, Program, Statement, TopLevel}; use std::str::FromStr; impl Program { - /// Evaluate the program, returning either an error or what it prints out when run. + /// Evaluate the program, returning either an error or a pair of the final value + /// produced and the output printed to the console. /// /// Doing this evaluation is particularly useful for testing, to ensure that if we /// modify a program in some way it does the same thing on both sides of the @@ -15,36 +16,51 @@ impl Program { /// Note that the errors here are slightly more strict that we enforce at runtime. /// For example, we check for overflow and underflow errors during evaluation, and /// we don't check for those in the compiled code. - pub fn eval(&self) -> Result { - let mut env = EvalEnvironment::empty(); + pub fn eval(&self) -> Result<(Value, String), EvalError> { + let mut env = ScopedMap::new(); let mut stdout = String::new(); + let mut last_result = Value::Void; for stmt in self.items.iter() { match stmt { - TopLevel::Function(_name, _arg_names, _body) => { - unimplemented!() - } - // at this point, evaluation is pretty simple. just walk through each - // statement, in order, and record printouts as we come to them. - TopLevel::Statement(Statement::Binding(_, name, value)) => { - let actual_value = value.eval(&env)?; - env = env.extend(name.clone().intern(), actual_value); + TopLevel::Function(name, arg_names, body) => { + last_result = Value::Closure( + name.clone().map(Name::intern), + env.clone(), + arg_names.iter().cloned().map(Name::intern).collect(), + body.clone(), + ); + if let Some(name) = name { + env.insert(name.clone().intern(), last_result.clone()); + } } - TopLevel::Statement(Statement::Print(_, name)) => { - let value = env.lookup(name.clone().intern())?; + TopLevel::Statement(Statement::Binding(_, name, value)) => { + let actual_value = value.eval(&env)?; + env.insert(name.clone().intern(), actual_value); + last_result = Value::Void; + } + + TopLevel::Statement(Statement::Print(loc, name)) => { + let value = env + .get(&name.clone().intern()) + .ok_or_else(|| EvalError::LookupFailed(loc.clone(), name.name.clone()))?; let line = format!("{} = {}\n", name, value); stdout.push_str(&line); + last_result = Value::Void; } } } - Ok(stdout) + Ok((last_result, stdout)) } } impl Expression { - fn eval(&self, env: &EvalEnvironment) -> Result { + fn eval( + &self, + env: &ScopedMap, Value>, + ) -> Result, EvalError> { match self { Expression::Value(_, v) => match v { super::Value::Number(_, ty, v) => match ty { @@ -61,7 +77,10 @@ impl Expression { }, }, - Expression::Reference(_, n) => Ok(env.lookup(ArcIntern::new(n.clone()))?), + Expression::Reference(loc, n) => env + .get(&ArcIntern::new(n.clone())) + .ok_or_else(|| EvalError::LookupFailed(loc.clone(), n.clone())) + .cloned(), Expression::Cast(_, target, expr) => { let target_type = PrimitiveType::from_str(target)?; @@ -86,13 +105,13 @@ impl Expression { #[test] fn two_plus_three() { let input = Program::parse(0, "x = 2 + 3; print x;").expect("parse works"); - let output = input.eval().expect("runs successfully"); + let (_, output) = input.eval().expect("runs successfully"); assert_eq!("x = 5u64\n", &output); } #[test] fn lotsa_math() { let input = Program::parse(0, "x = 2 + 3 * 10 / 5 - 1; print x;").expect("parse works"); - let output = input.eval().expect("runs successfully"); + let (_, output) = input.eval().expect("runs successfully"); assert_eq!("x = 7u64\n", &output); } diff --git a/src/type_infer.rs b/src/type_infer.rs index d47c7f4..080ce8d 100644 --- a/src/type_infer.rs +++ b/src/type_infer.rs @@ -42,9 +42,16 @@ impl syntax::Program { proptest::proptest! { #[test] fn translation_maintains_semantics(input in syntax::Program::arbitrary_with(GenerationEnvironment::new(false))) { - let syntax_result = input.eval(); + let syntax_result = input.eval().map(|(x,o)| (x.strip(), o)); let ir = input.type_infer().expect("arbitrary should generate type-safe programs"); - let ir_result = ir.eval(); - proptest::prop_assert!(syntax_result.eq(&ir_result)); + let ir_result = ir.eval().map(|(x,o)| (x.strip(), o)); + match (syntax_result, ir_result) { + (Err(e1), Err(e2)) => proptest::prop_assert_eq!(e1, e2), + (Ok((v1, o1)), Ok((v2, o2))) => { + proptest::prop_assert_eq!(v1, v2); + proptest::prop_assert_eq!(o1, o2); + } + _ => proptest::prop_assert!(false), + } } } diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index d67f3d8..5cfa210 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -77,7 +77,11 @@ pub fn convert_top_level( } let (expr, ty) = convert_expression(expr, constraint_db, renames, bindings); - constraint_db.push(Constraint::Equivalent(expr.location().clone(), rettype.clone(), ty)); + constraint_db.push(Constraint::Equivalent( + expr.location().clone(), + rettype.clone(), + ty, + )); ir::TopLevel::Function(funname, function_args, rettype, expr) } @@ -267,7 +271,10 @@ fn convert_expression( (last_call, ret_type) } else { prereqs.push(last_call); - (ir::Expression::Block(loc, ret_type.clone(), prereqs), ret_type) + ( + ir::Expression::Block(loc, ret_type.clone(), prereqs), + ret_type, + ) } } } diff --git a/src/type_infer/finalize.rs b/src/type_infer/finalize.rs index 577f4d8..59ff329 100644 --- a/src/type_infer/finalize.rs +++ b/src/type_infer/finalize.rs @@ -17,14 +17,14 @@ pub fn finalize_program( fn finalize_top_level(item: TopLevel, resolutions: &TypeResolutions) -> TopLevel { match item { - TopLevel::Function(name, args, rettype, expr) => { - TopLevel::Function( - name, - args.into_iter().map(|(name, t)| (name, finalize_type(t, resolutions))).collect(), - finalize_type(rettype, resolutions), - finalize_expression(expr, resolutions) - ) - } + TopLevel::Function(name, args, rettype, expr) => TopLevel::Function( + name, + args.into_iter() + .map(|(name, t)| (name, finalize_type(t, resolutions))) + .collect(), + finalize_type(rettype, resolutions), + finalize_expression(expr, resolutions), + ), TopLevel::Statement(expr) => TopLevel::Statement(finalize_expression(expr, resolutions)), } } @@ -53,7 +53,7 @@ fn finalize_expression( .collect(), ), - Expression::Block(loc, ty, mut exprs) => { + Expression::Block(loc, ty, exprs) => { let mut final_exprs = Vec::with_capacity(exprs.len()); for expr in exprs { diff --git a/src/util/scoped_map.rs b/src/util/scoped_map.rs index 69f442c..34e968b 100644 --- a/src/util/scoped_map.rs +++ b/src/util/scoped_map.rs @@ -1,6 +1,7 @@ use std::{borrow::Borrow, collections::HashMap, hash::Hash}; /// A version of [`std::collections::HashMap`] with a built-in notion of scope. +#[derive(Clone)] pub struct ScopedMap { scopes: Vec>, } @@ -84,4 +85,24 @@ impl ScopedMap { pub fn release_scope(&mut self) -> Option> { self.scopes.pop() } + + /// Create a new scoped set by mapping over the values of this one. + pub fn map_values(self, f: F) -> ScopedMap + where + F: Fn(V) -> W, + { + let mut scopes = Vec::with_capacity(self.scopes.len()); + + for scope in self.scopes { + let mut map = HashMap::with_capacity(scope.len()); + + for (k, v) in scope { + map.insert(k, f(v)); + } + + scopes.push(map); + } + + ScopedMap { scopes } + } } -- 2.53.0 From e5db6640f2d4b599a342e553912c8bc1d8687048 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Tue, 26 Dec 2023 21:08:01 -0800 Subject: [PATCH 09/59] checkpoint in reconstruction --- Cargo.toml | 38 ++-- build.rs | 17 +- src/backend/eval.rs | 6 +- src/backend/into_crane.rs | 3 +- src/backend/runtime.rs | 1 + src/bin/ngrun.rs | 74 +++++++ src/eval/primop.rs | 6 +- src/eval/primtype.rs | 84 ++++--- src/eval/value.rs | 4 +- src/ir/arbitrary.rs | 444 +++++++++++++++++++++++++++++++++++-- src/ir/ast.rs | 39 +++- src/ir/eval.rs | 26 ++- src/syntax.rs | 25 ++- src/syntax/arbitrary.rs | 4 +- src/syntax/eval.rs | 1 + src/syntax/parser.lalrpop | 4 +- src/syntax/pretty.rs | 1 + src/syntax/tokens.rs | 97 ++++---- src/type_infer/convert.rs | 5 + src/type_infer/finalize.rs | 5 + src/util/scoped_map.rs | 28 ++- 21 files changed, 759 insertions(+), 153 deletions(-) create mode 100644 src/bin/ngrun.rs diff --git a/Cargo.toml b/Cargo.toml index 9531890..296f284 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,25 +9,27 @@ name = "ngr" path = "src/lib.rs" [dependencies] -clap = { version = "^3.0.14", features = ["derive"] } +clap = { version = "4.4.11", features = ["derive"] } codespan = "0.11.1" codespan-reporting = "0.11.1" -cranelift-codegen = "0.99.2" -cranelift-jit = "0.99.2" -cranelift-frontend = "0.99.2" -cranelift-module = "0.99.2" -cranelift-native = "0.99.2" -cranelift-object = "0.99.2" -internment = { version = "0.7.0", default-features = false, features = ["arc"] } -lalrpop-util = "^0.20.0" -lazy_static = "^1.4.0" -logos = "^0.12.0" -pretty = { version = "^0.11.2", features = ["termcolor"] } -proptest = "^1.0.0" -rustyline = "^11.0.0" -target-lexicon = "^0.12.5" -tempfile = "^3.5.0" -thiserror = "^1.0.30" +cranelift-codegen = "0.103.0" +cranelift-jit = "0.103.0" +cranelift-frontend = "0.103.0" +cranelift-module = "0.103.0" +cranelift-native = "0.103.0" +cranelift-object = "0.103.0" +internment = { version = "0.7.4", default-features = false, features = ["arc"] } +lalrpop-util = "0.20.0" +lazy_static = "1.4.0" +logos = "0.13.0" +pretty = { version = "0.12.3", features = ["termcolor"] } +proptest = "1.4.0" +rand = "0.8.5" +rustyline = "13.0.0" +target-lexicon = "0.12.12" +tempfile = "3.8.1" +thiserror = "1.0.52" +anyhow = "1.0.77" [build-dependencies] -lalrpop = "^0.20.0" +lalrpop = "0.20.0" diff --git a/build.rs b/build.rs index 2f4266e..f883c2b 100644 --- a/build.rs +++ b/build.rs @@ -72,13 +72,28 @@ fn generate_tests(f: &mut File, path_so_far: PathBuf) -> std::io::Result<()> { f, " assert_eq!(errors.len(), 0, \"file should have no validation errors\");" )?; + writeln!(f, " let syntax_result = syntax.eval();")?; writeln!( f, " let ir = syntax.type_infer().expect(\"example is typed correctly\");" )?; writeln!(f, " let ir_result = ir.eval();")?; + writeln!(f, " match (&syntax_result, &ir_result) {{")?; + writeln!(f, " (Err(e1), Err(e2)) => assert_eq!(e1, e2),")?; + writeln!(f, " (Ok((v1, o1)), Ok((v2, o2))) => {{")?; + writeln!(f, " assert_eq!(v1, v2);")?; + writeln!(f, " assert_eq!(o1, o2);")?; + writeln!(f, " }}")?; + writeln!(f, " _ => panic!(\"mismatched outputs, {{:?}} and {{:?}}\", syntax_result, ir_result)")?; + writeln!(f, " }}")?; writeln!(f, " let compiled_result = Backend::::eval(ir);")?; - writeln!(f, " assert_eq!(ir_result, compiled_result);")?; + writeln!(f, " match (&compiled_result, &ir_result) {{")?; + writeln!(f, " (Err(e1), Err(e2)) => assert_eq!(e1, e2),")?; + writeln!(f, " (Ok(o1), Ok((_, o2))) => {{")?; + writeln!(f, " assert_eq!(o1, o2);")?; + writeln!(f, " }}")?; + writeln!(f, " _ => panic!(\"mismatched outputs, {{:?}} and {{:?}}\", compiled_result, ir_result)")?; + writeln!(f, " }}")?; } writeln!(f, "}}")?; } diff --git a/src/backend/eval.rs b/src/backend/eval.rs index de7a1fa..7768ef8 100644 --- a/src/backend/eval.rs +++ b/src/backend/eval.rs @@ -1,8 +1,6 @@ use crate::backend::Backend; use crate::eval::EvalError; use crate::ir::{Expression, Program, TopLevel, Type}; -#[cfg(test)] -use crate::syntax::arbitrary::GenerationEnvironment; use crate::syntax::Location; use cranelift_jit::JITModule; use cranelift_object::ObjectModule; @@ -176,7 +174,7 @@ proptest::proptest! { // without error, assuming any possible input ... well, any possible input that // doesn't involve overflow or underflow. #[test] - fn static_backend(program in Program::arbitrary_with(GenerationEnvironment::new(false))) { + fn static_backend(program in Program::arbitrary()) { use crate::eval::PrimOpError; let basic_result = program.eval().map(|(_,x)| x); @@ -206,7 +204,7 @@ proptest::proptest! { // without error, assuming any possible input ... well, any possible input that // doesn't involve overflow or underflow. #[test] - fn jit_backend(program in Program::arbitrary_with(GenerationEnvironment::new(false))) { + fn jit_backend(program in Program::arbitrary()) { use crate::eval::PrimOpError; // use pretty::{DocAllocator, Pretty}; // let allocator = pretty::BoxAllocator; diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index d897f7c..1b409f6 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -338,7 +338,7 @@ impl Backend { let vtype_repr = builder.ins().iconst(types::I64, vtype as i64); let casted_val = match vtype { - ConstantType::U64 | ConstantType::I64 => val, + ConstantType::U64 | ConstantType::I64 | ConstantType::Void => val, ConstantType::I8 | ConstantType::I16 | ConstantType::I32 => { builder.ins().sextend(types::I64, val) } @@ -401,6 +401,7 @@ impl Backend { builder.ins().iconst(types::I64, v as i64), ConstantType::U64, )), + Value::Void => Ok((builder.ins().iconst(types::I64, 0i64), ConstantType::Void)), }, ValueOrRef::Ref(_, _, name) => match variables.get(&name) { None => Err(BackendError::VariableLookupFailure(name)), diff --git a/src/backend/runtime.rs b/src/backend/runtime.rs index 40907aa..1df45a3 100644 --- a/src/backend/runtime.rs +++ b/src/backend/runtime.rs @@ -110,6 +110,7 @@ extern "C" fn runtime_print( let reconstituted = cstr.to_string_lossy(); let output = match vtype_repr.try_into() { + Ok(ConstantType::Void) => format!("{} = ", reconstituted), Ok(ConstantType::I8) => format!("{} = {}i8", reconstituted, value as i8), Ok(ConstantType::I16) => format!("{} = {}i16", reconstituted, value as i16), Ok(ConstantType::I32) => format!("{} = {}i32", reconstituted, value as i32), diff --git a/src/bin/ngrun.rs b/src/bin/ngrun.rs new file mode 100644 index 0000000..9230f63 --- /dev/null +++ b/src/bin/ngrun.rs @@ -0,0 +1,74 @@ +use clap::Parser; +use codespan_reporting::files::SimpleFiles; +use ngr::eval::Value; +use ngr::syntax; +use ngr::type_infer::TypeInferenceResult; +use pretty::termcolor::StandardStream; + +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +struct CommandLineArguments { + /// Which interpreter to use: syntax, ir, or jit + #[arg(value_enum)] + interpreter: Interpreter, + + /// The file to parse + file: String, +} + +#[allow(clippy::upper_case_acronyms)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)] +enum Interpreter { + /// Run the syntax-level interpreter + Syntax, + /// Run the IR-level interpreter + IR, + /// Run the JIT backend + JIT, +} + +fn print_result(result: (Value, String)) { + println!("{}", result.1); + println!("RESULT: {}", result.0); +} + +fn main() -> Result<(), anyhow::Error> { + let cli = CommandLineArguments::parse(); + let mut file_database = SimpleFiles::new(); + let mut console = StandardStream::stdout(pretty::termcolor::ColorChoice::Auto); + let console_options = codespan_reporting::term::Config::default(); + let syntax = syntax::Program::parse_file(&mut file_database, cli.file.as_ref())?; + let mut emit = |x| { + let _ = codespan_reporting::term::emit(&mut console, &console_options, &file_database, &x); + }; + + if cli.interpreter == Interpreter::Syntax { + print_result(syntax.eval()?); + return Ok(()); + } + + let ir = match syntax.type_infer() { + TypeInferenceResult::Success { result, warnings } => { + for warning in warnings { + emit(warning.into()); + } + result + } + TypeInferenceResult::Failure { errors, warnings } => { + for warning in warnings { + emit(warning.into()); + } + for error in errors { + emit(error.into()); + } + return Err(anyhow::anyhow!("failed to infer program types")); + } + }; + + if cli.interpreter == Interpreter::IR { + print_result(ir.eval()?); + return Ok(()); + } + + unimplemented!(); +} \ No newline at end of file diff --git a/src/eval/primop.rs b/src/eval/primop.rs index c4fcd25..aa9ffef 100644 --- a/src/eval/primop.rs +++ b/src/eval/primop.rs @@ -36,14 +36,16 @@ pub enum PrimOpError { ValuePrimitiveTypeError(#[from] ValuePrimitiveTypeError), } -impl PartialEq> for PrimOpError { +impl PartialEq> for PrimOpError { fn eq(&self, other: &PrimOpError) -> bool { match (self, other) { (PrimOpError::MathFailure(a), PrimOpError::MathFailure(b)) => a == b, (PrimOpError::TypeMismatch(a, b, c), PrimOpError::TypeMismatch(x, y, z)) => { a == x && b.strip() == y.strip() && c.strip() == z.strip() } - (PrimOpError::BadTypeFor(a, b), PrimOpError::BadTypeFor(x, y)) => a == x && b.strip() == y.strip(), + (PrimOpError::BadTypeFor(a, b), PrimOpError::BadTypeFor(x, y)) => { + a == x && b.strip() == y.strip() + } (PrimOpError::BadArgCount(a, b), PrimOpError::BadArgCount(x, y)) => a == x && b == y, (PrimOpError::UnknownPrimOp(a), PrimOpError::UnknownPrimOp(x)) => a == x, ( diff --git a/src/eval/primtype.rs b/src/eval/primtype.rs index 0876310..3ed2b1c 100644 --- a/src/eval/primtype.rs +++ b/src/eval/primtype.rs @@ -63,6 +63,7 @@ impl<'a, IR> TryFrom<&'a Value> for PrimitiveType { impl From for PrimitiveType { fn from(value: ConstantType) -> Self { match value { + ConstantType::Void => PrimitiveType::Void, ConstantType::I8 => PrimitiveType::I8, ConstantType::I16 => PrimitiveType::I16, ConstantType::I32 => PrimitiveType::I32, @@ -100,44 +101,43 @@ impl FromStr for PrimitiveType { } impl PrimitiveType { + /// Return the set of types that this type can be safely cast to + pub fn allowed_casts(&self) -> &'static [PrimitiveType] { + match self { + PrimitiveType::Void => &[PrimitiveType::Void], + PrimitiveType::U8 => &[ + PrimitiveType::U8, + PrimitiveType::U16, + PrimitiveType::U32, + PrimitiveType::U64, + PrimitiveType::I16, + PrimitiveType::I32, + PrimitiveType::I64, + ], + PrimitiveType::U16 => &[ + PrimitiveType::U16, + PrimitiveType::U32, + PrimitiveType::U64, + PrimitiveType::I32, + PrimitiveType::I64, + ], + PrimitiveType::U32 => &[PrimitiveType::U32, PrimitiveType::U64, PrimitiveType::I64], + PrimitiveType::U64 => &[PrimitiveType::U64], + PrimitiveType::I8 => &[ + PrimitiveType::I8, + PrimitiveType::I16, + PrimitiveType::I32, + PrimitiveType::I64, + ], + PrimitiveType::I16 => &[PrimitiveType::I16, PrimitiveType::I32, PrimitiveType::I64], + PrimitiveType::I32 => &[PrimitiveType::I32, PrimitiveType::I64], + PrimitiveType::I64 => &[PrimitiveType::I64], + } + } + /// Return true if this type can be safely cast into the target type. pub fn can_cast_to(&self, target: &PrimitiveType) -> bool { - match self { - PrimitiveType::Void => matches!(target, PrimitiveType::Void), - PrimitiveType::U8 => matches!( - target, - PrimitiveType::U8 - | PrimitiveType::U16 - | PrimitiveType::U32 - | PrimitiveType::U64 - | PrimitiveType::I16 - | PrimitiveType::I32 - | PrimitiveType::I64 - ), - PrimitiveType::U16 => matches!( - target, - PrimitiveType::U16 - | PrimitiveType::U32 - | PrimitiveType::U64 - | PrimitiveType::I32 - | PrimitiveType::I64 - ), - PrimitiveType::U32 => matches!( - target, - PrimitiveType::U32 | PrimitiveType::U64 | PrimitiveType::I64 - ), - PrimitiveType::U64 => target == &PrimitiveType::U64, - PrimitiveType::I8 => matches!( - target, - PrimitiveType::I8 | PrimitiveType::I16 | PrimitiveType::I32 | PrimitiveType::I64 - ), - PrimitiveType::I16 => matches!( - target, - PrimitiveType::I16 | PrimitiveType::I32 | PrimitiveType::I64 - ), - PrimitiveType::I32 => matches!(target, PrimitiveType::I32 | PrimitiveType::I64), - PrimitiveType::I64 => target == &PrimitiveType::I64, - } + self.allowed_casts().contains(target) } /// Try to cast the given value to this type, returning the new value. @@ -192,4 +192,16 @@ impl PrimitiveType { PrimitiveType::I64 => Some(i64::MAX as u64), } } + + pub fn valid_operators(&self) -> &'static [(&'static str, usize)] { + match self { + PrimitiveType::Void => &[], + PrimitiveType::U8 | PrimitiveType::U16 | PrimitiveType::U32 | PrimitiveType::U64 => { + &[("+", 2), ("-", 2), ("*", 2), ("/", 2)] + } + PrimitiveType::I8 | PrimitiveType::I16 | PrimitiveType::I32 | PrimitiveType::I64 => { + &[("+", 2), ("-", 1), ("-", 2), ("*", 2), ("/", 2)] + } + } + } } diff --git a/src/eval/value.rs b/src/eval/value.rs index 26c020b..c31d1ed 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -44,9 +44,7 @@ impl Value { Value::I32(x) => Value::I32(*x), Value::I64(x) => Value::I64(*x), Value::Closure(name, env, args, _) => { - let new_env = env - .clone() - .map_values(|x| x.strip()); + let new_env = env.clone().map_values(|x| x.strip()); Value::Closure(name.clone(), new_env, args.clone(), ()) } } diff --git a/src/ir/arbitrary.rs b/src/ir/arbitrary.rs index 94f925a..bf531a5 100644 --- a/src/ir/arbitrary.rs +++ b/src/ir/arbitrary.rs @@ -1,21 +1,433 @@ -use crate::ir::{Program, TopLevel, Expression, ValueOrRef, Value, Type}; -use proptest::{ - prelude::Arbitrary, - strategy::{BoxedStrategy, Strategy}, -}; +use crate::eval::PrimitiveType; +use crate::ir::{Expression, Primitive, Program, TopLevel, Type, Value, ValueOrRef, Variable}; +use crate::syntax::Location; +use crate::util::scoped_map::ScopedMap; +use proptest::strategy::{NewTree, Strategy, ValueTree}; +use proptest::test_runner::{TestRng, TestRunner}; +use rand::distributions::{Distribution, WeightedIndex}; +use rand::seq::SliceRandom; +use rand::Rng; +use std::str::FromStr; -impl Arbitrary for Program { - type Parameters = crate::syntax::arbitrary::GenerationEnvironment; - type Strategy = BoxedStrategy; +lazy_static::lazy_static! { + static ref PROGRAM_LENGTH_DISTRIBUTION: WeightedIndex = WeightedIndex::new([ + 0, // % chance of 0 + 10, // % chance of 1 + 10, // % chance of 2 + 15, // % chance of 3 + 10, // % chance of 4 + 10, // % chance of 5 + 10, // % chance of 6 + 5, // % chance of 7 + 5, // % chance of 8 + 5, // % chance of 9 + 5, // % chance of 10 + 5, // % chance of 11 + 3, // % chance of 12 + 3, // % chance of 13 + 3, // % chance of 14 + 1, // % chance of 15 + ]).unwrap(); - fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - unimplemented!() - //crate::syntax::Program::arbitrary_with(args) - // .prop_map(|x| { - // x.type_infer() - // .expect("arbitrary_with should generate type-correct programs") - // }) - // .boxed() + static ref BLOCK_LENGTH_DISTRIBUTION: WeightedIndex = WeightedIndex::new([ + 1, // % chance of 0 + 10, // % chance of 1 + 20, // % chance of 2 + 15, // % chance of 3 + 10, // % chance of 4 + 8, // % chance of 5 + 8, // % chance of 6 + 5, // % chance of 7 + 5, // % chance of 8 + 4, // % chance of 9 + 3, // % chance of 10 + 3, // % chance of 11 + 3, // % chance of 12 + 2, // % chance of 13 + 2, // % chance of 14 + 1, // % chance of 15 + ]).unwrap(); + + static ref FUNCTION_ARGUMENTS_DISTRIBUTION: WeightedIndex = WeightedIndex::new([ + 5, // % chance of 0 + 20, // % chance of 1 + 20, // % chance of 2 + 20, // % chance of 3 + 15, // % chance of 4 + 10, // % chance of 5 + 5, // % chance of 6 + 2, // % chance of 7 + 1, // % chance of 8 + 1, // % chance of 9 + 1, // % chance of 10 + ]).unwrap(); + + static ref STATEMENT_TYPE_DISTRIBUTION: WeightedIndex = WeightedIndex::new( + STATEMENT_TYPE_FREQUENCIES.iter().map(|x| x.1) + ).unwrap(); + + static ref EXPRESSION_TYPE_DISTRIBUTION: WeightedIndex = WeightedIndex::new( + EXPRESSION_TYPE_FREQUENCIES.iter().map(|x| x.1) + ).unwrap(); + + static ref ARGUMENT_TYPE_DISTRIBUTION: WeightedIndex = WeightedIndex::new( + ARGUMENT_TYPE_FREQUENCIES.iter().map(|x| x.1) + ).unwrap(); + + static ref VALUE_TYPE_DISTRIBUTION: WeightedIndex = WeightedIndex::new( + VALUE_TYPE_FREQUENCIES.iter().map(|x| x.1) + ).unwrap(); +} + +static STATEMENT_TYPE_FREQUENCIES: &[(StatementType, usize)] = &[ + (StatementType::Binding, 3), + (StatementType::Function, 1), + (StatementType::Expression, 2), +]; + +static EXPRESSION_TYPE_FREQUENCIES: &[(ExpressionType, usize)] = &[ + (ExpressionType::Atomic, 50), + (ExpressionType::Cast, 5), + (ExpressionType::Primitive, 5), + (ExpressionType::Block, 10), + (ExpressionType::Print, 10), + (ExpressionType::Bind, 20), +]; + +static ARGUMENT_TYPE_FREQUENCIES: &[(Type, usize)] = &[ + (Type::Primitive(PrimitiveType::U8), 1), + (Type::Primitive(PrimitiveType::U16), 1), + (Type::Primitive(PrimitiveType::U32), 1), + (Type::Primitive(PrimitiveType::U64), 1), + (Type::Primitive(PrimitiveType::I8), 1), + (Type::Primitive(PrimitiveType::I16), 1), + (Type::Primitive(PrimitiveType::I32), 1), + (Type::Primitive(PrimitiveType::I64), 1), +]; + +enum StatementType { + Binding, + Function, + Expression, +} + +enum ExpressionType { + Atomic, + Cast, + Primitive, + Block, + Print, + Bind, +} + +// this knowingly excludes void +static VALUE_TYPE_FREQUENCIES: &[(ValueType, usize)] = &[ + (ValueType::I8, 1), + (ValueType::I16, 1), + (ValueType::I32, 1), + (ValueType::I64, 1), + (ValueType::U8, 1), + (ValueType::U16, 1), + (ValueType::U32, 1), + (ValueType::U64, 1), +]; + +#[derive(Copy, Clone)] +enum ValueType { + I8, + I16, + I32, + I64, + U8, + U16, + U32, + U64, + Void, +} + +impl From for ValueType { + fn from(value: PrimitiveType) -> Self { + match value { + PrimitiveType::U8 => ValueType::U8, + PrimitiveType::U16 => ValueType::U16, + PrimitiveType::U32 => ValueType::U32, + PrimitiveType::U64 => ValueType::U64, + PrimitiveType::I8 => ValueType::I8, + PrimitiveType::I16 => ValueType::I16, + PrimitiveType::I32 => ValueType::I32, + PrimitiveType::I64 => ValueType::I64, + PrimitiveType::Void => ValueType::Void, + } } } +#[derive(Debug, Default)] +pub struct ProgramGenerator {} + +impl Strategy for ProgramGenerator { + type Tree = ProgramTree; + type Value = Program; + + fn new_tree(&self, runner: &mut TestRunner) -> NewTree { + NewTree::::Ok(ProgramTree::new(runner.new_rng())) + } +} + +pub struct ProgramTree { + _rng: TestRng, + current: Program, +} + +impl ProgramTree { + fn new(mut rng: TestRng) -> Self { + let mut items = vec![]; + let program_length = PROGRAM_LENGTH_DISTRIBUTION.sample(&mut rng); + let mut env = ScopedMap::new(); + + for _ in 0..program_length { + match STATEMENT_TYPE_FREQUENCIES[STATEMENT_TYPE_DISTRIBUTION.sample(&mut rng)].0 { + StatementType::Binding => { + let binding = generate_random_binding(&mut rng, &mut env); + items.push(TopLevel::Statement(binding)); + } + StatementType::Expression => { + let expr = generate_random_expression(&mut rng, &mut env); + items.push(TopLevel::Statement(expr)); + } + StatementType::Function => { + env.new_scope(); + let name = generate_random_name(&mut rng); + let mut args = vec![]; + let arg_count = FUNCTION_ARGUMENTS_DISTRIBUTION.sample(&mut rng); + for _ in 0..arg_count { + let name = generate_random_name(&mut rng); + let ty = generate_random_argument_type(&mut rng); + args.push((name, ty)); + } + let body = generate_random_expression(&mut rng, &mut env); + let rettype = body.type_of(); + env.release_scope(); + items.push(TopLevel::Function(name, args, rettype, body)) + } + } + } + + let current = Program { items }; + + ProgramTree { _rng: rng, current } + } +} + +impl ValueTree for ProgramTree { + type Value = Program; + + fn current(&self) -> Self::Value { + self.current.clone() + } + + fn simplify(&mut self) -> bool { + unimplemented!() + } + + fn complicate(&mut self) -> bool { + unimplemented!() + } +} + +#[derive(Debug)] +struct ExpressionGenerator {} + +impl Strategy for ExpressionGenerator { + type Tree = ExpressionTree; + type Value = Expression; + + fn new_tree(&self, runner: &mut TestRunner) -> NewTree { + NewTree::::Ok(ExpressionTree::new(runner.new_rng())) + } +} + +struct ExpressionTree { + _rng: TestRng, + current: Expression, +} + +impl ValueTree for ExpressionTree { + type Value = Expression; + + fn current(&self) -> Self::Value { + self.current.clone() + } + + fn simplify(&mut self) -> bool { + unimplemented!() + } + + fn complicate(&mut self) -> bool { + unimplemented!() + } +} + +impl ExpressionTree { + fn new(mut rng: TestRng) -> Self { + let mut env = ScopedMap::new(); + let current = generate_random_expression(&mut rng, &mut env); + + ExpressionTree { _rng: rng, current } + } +} + +fn generate_random_expression( + rng: &mut TestRng, + env: &mut ScopedMap, +) -> Expression { + match EXPRESSION_TYPE_FREQUENCIES[EXPRESSION_TYPE_DISTRIBUTION.sample(rng)].0 { + ExpressionType::Atomic => Expression::Atomic(generate_random_valueref(rng, env, None)), + + ExpressionType::Bind => generate_random_binding(rng, env), + + ExpressionType::Block => { + let num_stmts = BLOCK_LENGTH_DISTRIBUTION.sample(rng); + let mut stmts = Vec::new(); + let mut last_type = Type::Primitive(PrimitiveType::Void); + + env.new_scope(); + for _ in 0..num_stmts { + let next = generate_random_expression(rng, env); + last_type = next.type_of(); + stmts.push(next); + } + env.release_scope(); + + Expression::Block(Location::manufactured(), last_type, stmts) + } + + ExpressionType::Cast => { + let inner = generate_random_valueref(rng, env, None); + + match inner.type_of() { + // nevermind + Type::Function(_, _) => Expression::Atomic(inner), + Type::Primitive(primty) => { + let to_type = primty + .allowed_casts() + .choose(rng) + .expect("actually chose type"); + Expression::Cast(Location::manufactured(), Type::Primitive(*to_type), inner) + } + } + } + + ExpressionType::Primitive => { + let base_expr = generate_random_valueref(rng, env, None); + let out_type = base_expr.type_of(); + + match out_type { + Type::Function(_, _) => Expression::Atomic(base_expr), + Type::Primitive(primty) => match primty.valid_operators().choose(rng) { + None => Expression::Atomic(base_expr), + Some((operator, arg_count)) => { + let primop = Primitive::from_str(operator).expect("chose valid primitive"); + let mut args = vec![base_expr]; + + while args.len() < *arg_count { + args.push(generate_random_valueref(rng, env, Some(primty))); + } + + Expression::Primitive(Location::manufactured(), out_type, primop, args) + } + }, + } + } + + ExpressionType::Print => { + let possible_variables = env + .bindings() + .iter() + .filter_map(|(variable, ty)| { + if ty.is_printable() { + Some(variable.clone()) + } else { + None + } + }) + .collect::>(); + + if possible_variables.is_empty() { + generate_random_binding(rng, env) + } else { + Expression::Print( + Location::manufactured(), + possible_variables.choose(rng).unwrap().clone(), + ) + } + } + } +} + +fn generate_random_binding( + rng: &mut TestRng, + env: &mut ScopedMap, +) -> Expression { + let name = generate_random_name(rng); + let expr = generate_random_expression(rng, env); + let ty = expr.type_of(); + env.insert(name.clone(), ty.clone()); + Expression::Bind(Location::manufactured(), name, ty, Box::new(expr)) +} + +fn generate_random_valueref( + rng: &mut TestRng, + env: &mut ScopedMap, + target_type: Option, +) -> ValueOrRef { + let mut bindings = env.bindings(); + + bindings.retain(|_, value| { + target_type + .map(|x| value == &Type::Primitive(x)) + .unwrap_or(true) + }); + + if rng.gen() || bindings.is_empty() { + let value_type = if let Some(target_type) = target_type { + ValueType::from(target_type) + } else { + VALUE_TYPE_FREQUENCIES[VALUE_TYPE_DISTRIBUTION.sample(rng)].0 + }; + + // generate a constant + let val = match value_type { + ValueType::I8 => Value::I8(None, rng.gen()), + ValueType::I16 => Value::I16(None, rng.gen()), + ValueType::I32 => Value::I32(None, rng.gen()), + ValueType::I64 => Value::I64(None, rng.gen()), + ValueType::U8 => Value::U8(None, rng.gen()), + ValueType::U16 => Value::U16(None, rng.gen()), + ValueType::U32 => Value::U32(None, rng.gen()), + ValueType::U64 => Value::U64(None, rng.gen()), + ValueType::Void => Value::Void, + }; + + ValueOrRef::Value(Location::manufactured(), val.type_of(), val) + } else { + // generate a reference + let weighted_keys = bindings.keys().map(|x| (1, x)).collect::>(); + let distribution = WeightedIndex::new(weighted_keys.iter().map(|x| x.0)).unwrap(); + let var = weighted_keys[distribution.sample(rng)].1.clone(); + let ty = bindings + .remove(&var) + .expect("chose unbound variable somehow?"); + ValueOrRef::Ref(Location::manufactured(), ty, var) + } +} + +fn generate_random_name(rng: &mut TestRng) -> Variable { + let start = rng.gen_range('a'..='z'); + crate::ir::gensym(&format!("{}", start)) +} + +fn generate_random_argument_type(rng: &mut TestRng) -> Type { + ARGUMENT_TYPE_FREQUENCIES[ARGUMENT_TYPE_DISTRIBUTION.sample(rng)] + .0 + .clone() +} diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 67c8b12..62737fd 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -5,8 +5,11 @@ use crate::{ }; use internment::ArcIntern; use pretty::{BoxAllocator, DocAllocator, Pretty}; +use proptest::arbitrary::Arbitrary; use std::{fmt, str::FromStr, sync::atomic::AtomicUsize}; +use super::arbitrary::ProgramGenerator; + /// We're going to represent variables as interned strings. /// /// These should be fast enough for comparison that it's OK, since it's going to end up @@ -45,7 +48,7 @@ pub fn gensym(base: &str) -> Variable { /// The type variable is, somewhat confusingly, the current definition of a type within /// the IR. Since the makeup of this structure may change over the life of the compiler, /// it's easiest to just make it an argument. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Program { // For now, a program is just a vector of statements. In the future, we'll probably // extend this to include a bunch of other information, but for now: just a list. @@ -74,12 +77,21 @@ where } } +impl Arbitrary for Program { + type Parameters = (); + type Strategy = ProgramGenerator; + + fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { + ProgramGenerator::default() + } +} + /// A thing that can sit at the top level of a file. /// /// For the moment, these are statements and functions. Other things /// will likely be added in the future, but for now: just statements /// and functions -#[derive(Debug)] +#[derive(Clone, Debug)] pub enum TopLevel { Statement(Expression), Function(Variable, Vec<(Variable, Type)>, Type, Expression), @@ -140,8 +152,7 @@ impl Expression { /// computed. pub fn type_of(&self) -> Type { match self { - Expression::Atomic(ValueOrRef::Ref(_, t, _)) => t.clone(), - Expression::Atomic(ValueOrRef::Value(_, t, _)) => t.clone(), + Expression::Atomic(x) => x.type_of(), Expression::Cast(_, t, _) => t.clone(), Expression::Primitive(_, t, _, _) => t.clone(), Expression::Block(_, t, _) => t.clone(), @@ -300,6 +311,15 @@ where } } +impl ValueOrRef { + pub fn type_of(&self) -> Type { + match self { + ValueOrRef::Ref(_, t, _) => t.clone(), + ValueOrRef::Value(_, t, _) => t.clone(), + } + } +} + impl From> for Expression { fn from(value: ValueOrRef) -> Self { Expression::Atomic(value) @@ -322,6 +342,7 @@ pub enum Value { U16(Option, u16), U32(Option, u32), U64(Option, u64), + Void, } impl Value { @@ -336,6 +357,7 @@ impl Value { Value::U16(_, _) => Type::Primitive(PrimitiveType::U16), Value::U32(_, _) => Type::Primitive(PrimitiveType::U32), Value::U64(_, _) => Type::Primitive(PrimitiveType::U64), + Value::Void => Type::void(), } } } @@ -379,6 +401,7 @@ where pretty_internal(opt_base, *value as u64, ConstantType::U32) } Value::U64(opt_base, value) => pretty_internal(opt_base, *value, ConstantType::U64), + Value::Void => allocator.text(""), } } } @@ -389,6 +412,14 @@ pub enum Type { Function(Vec, Box), } +impl Type { + /// Returns true if this variable can reasonably be passed to the print + /// expression for printing. + pub fn is_printable(&self) -> bool { + matches!(self, Type::Primitive(_)) + } +} + impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Type where A: 'a, diff --git a/src/ir/eval.rs b/src/ir/eval.rs index dd6340d..30bf920 100644 --- a/src/ir/eval.rs +++ b/src/ir/eval.rs @@ -32,7 +32,7 @@ impl> Program { } TopLevel::Statement(expr) => { - last_value = expr.eval(&env, &mut stdout)?; + last_value = expr.eval(&mut env, &mut stdout)?; } } } @@ -47,7 +47,7 @@ where { fn eval( &self, - env: &ScopedMap>, + env: &mut ScopedMap>, stdout: &mut String, ) -> Result, IREvalError> { match self { @@ -79,8 +79,14 @@ where } } - Expression::Block(_, _, _) => { - unimplemented!() + Expression::Block(_, _, stmts) => { + let mut result = Value::Void; + + for stmt in stmts.iter() { + result = stmt.eval(env, stdout)?; + } + + Ok(result) } Expression::Print(loc, n) => { @@ -92,16 +98,17 @@ where Ok(Value::Void) } - Expression::Bind(_, _, _, _) => unimplemented!(), + Expression::Bind(_, name, _, value) => { + let value = value.eval(env, stdout)?; + env.insert(name.clone(), value); + Ok(Value::Void) + } } } } impl ValueOrRef { - fn eval( - &self, - env: &ScopedMap>, - ) -> Result, IREvalError> { + fn eval(&self, env: &ScopedMap>) -> Result, IREvalError> { match self { ValueOrRef::Value(_, _, v) => match v { super::Value::I8(_, v) => Ok(Value::I8(*v)), @@ -112,6 +119,7 @@ impl ValueOrRef { super::Value::U16(_, v) => Ok(Value::U16(*v)), super::Value::U32(_, v) => Ok(Value::U32(*v)), super::Value::U64(_, v) => Ok(Value::U64(*v)), + super::Value::Void => Ok(Value::Void), }, ValueOrRef::Ref(loc, _, n) => env diff --git a/src/syntax.rs b/src/syntax.rs index a33119b..0047573 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -51,6 +51,7 @@ use ::pretty::{Arena, Pretty}; use lalrpop_util::ParseError; #[cfg(test)] use proptest::{arbitrary::Arbitrary, prop_assert, prop_assert_eq}; +use std::ops::Range; #[cfg(test)] use std::str::FromStr; use thiserror::Error; @@ -105,7 +106,7 @@ impl ParserError { /// closely. The major thing we do here is convert [`lalrpop`]'s notion of a location, /// which is just an offset that it got from the lexer, into an actual location that /// we can use in our [`Diagnostic`]s. - fn convert(file_idx: usize, err: ParseError) -> Self { + fn convert(file_idx: usize, err: ParseError) -> Self { match err { ParseError::InvalidToken { location } => { ParserError::InvalidToken(Location::new(file_idx, location..location + 1)) @@ -123,11 +124,7 @@ impl ParserError { ParseError::ExtraToken { token: (start, token, end), } => ParserError::ExtraToken(Location::new(file_idx, start..end), token), - ParseError::User { error } => match error { - LexerError::LexFailure(offset) => { - ParserError::LexFailure(Location::new(file_idx, offset..offset + 1)) - } - }, + ParseError::User { error } => error, } } } @@ -236,7 +233,7 @@ impl Program { pub fn parse(file_idx: usize, buffer: &str) -> Result { let lexer = Token::lexer(buffer) .spanned() - .map(|(token, range)| (range.start, token, range.end)); + .map(|x| permute_lexer_result(file_idx, x)); ProgramParser::new() .parse(file_idx, lexer) .map_err(|e| ParserError::convert(file_idx, e)) @@ -253,13 +250,25 @@ impl TopLevel { pub fn parse(file_idx: usize, buffer: &str) -> Result { let lexer = Token::lexer(buffer) .spanned() - .map(|(token, range)| (range.start, token, range.end)); + .map(|x| permute_lexer_result(file_idx, x)); TopLevelParser::new() .parse(file_idx, lexer) .map_err(|e| ParserError::convert(file_idx, e)) } } +fn permute_lexer_result( + file_idx: usize, + result: (Result, Range), +) -> Result<(usize, Token, usize), ParserError> { + let (token, range) = result; + + match token { + Ok(v) => Ok((range.start, v, range.end)), + Err(()) => Err(ParserError::LexFailure(Location::new(file_idx, range))), + } +} + #[cfg(test)] impl FromStr for Program { type Err = ParserError; diff --git a/src/syntax/arbitrary.rs b/src/syntax/arbitrary.rs index 7ee27a1..cbe03ad 100644 --- a/src/syntax/arbitrary.rs +++ b/src/syntax/arbitrary.rs @@ -8,11 +8,12 @@ use proptest::{ use std::collections::HashMap; use std::ops::Range; -const VALID_VARIABLE_NAMES: &str = r"[a-z][a-zA-Z0-9_]*"; +pub const VALID_VARIABLE_NAMES: &str = r"[a-z][a-zA-Z0-9_]*"; impl ConstantType { fn get_operators(&self) -> &'static [(&'static str, usize)] { match self { + ConstantType::Void => &[], ConstantType::I8 | ConstantType::I16 | ConstantType::I32 | ConstantType::I64 => { &[("+", 2), ("-", 1), ("-", 2), ("*", 2), ("/", 2)] } @@ -209,6 +210,7 @@ impl Arbitrary for Value { (printed_base_strategy, bool::arbitrary(), value_strategy) .prop_map(move |(base, declare_type, value)| { let converted_value = match genenv.return_type { + ConstantType::Void => value, ConstantType::I8 => value % (i8::MAX as u64), ConstantType::U8 => value % (u8::MAX as u64), ConstantType::I16 => value % (i16::MAX as u64), diff --git a/src/syntax/eval.rs b/src/syntax/eval.rs index 8025d07..5813702 100644 --- a/src/syntax/eval.rs +++ b/src/syntax/eval.rs @@ -66,6 +66,7 @@ impl Expression { super::Value::Number(_, ty, v) => match ty { None => Ok(Value::U64(*v)), // FIXME: make these types validate their input size + Some(ConstantType::Void) => Ok(Value::Void), Some(ConstantType::I8) => Ok(Value::I8(*v as i8)), Some(ConstantType::I16) => Ok(Value::I16(*v as i16)), Some(ConstantType::I32) => Ok(Value::I32(*v as i32)), diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index d433571..40987f3 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -8,7 +8,7 @@ //! (Although, at some point, things can become so complicated that you might //! eventually want to leave lalrpop behind.) //! -use crate::syntax::{LexerError, Location}; +use crate::syntax::{Location, ParserError}; use crate::syntax::ast::{Program,TopLevel,Statement,Expression,Value,Name}; use crate::syntax::tokens::{ConstantType, Token}; use internment::ArcIntern; @@ -24,7 +24,7 @@ grammar(file_idx: usize); extern { type Location = usize; // Logos, our lexer, implements locations as // offsets from the start of the file. - type Error = LexerError; + type Error = ParserError; // here we redeclare all of the tokens. enum Token { diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index c52a827..9bb0461 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -135,6 +135,7 @@ where fn type_suffix(x: &Option) -> &'static str { match x { None => "", + Some(ConstantType::Void) => "", Some(ConstantType::I8) => "i8", Some(ConstantType::I16) => "i16", Some(ConstantType::I32) => "i32", diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs index f6da79e..5a2bb99 100644 --- a/src/syntax/tokens.rs +++ b/src/syntax/tokens.rs @@ -1,7 +1,6 @@ use internment::ArcIntern; use logos::{Lexer, Logos}; use std::fmt; -use std::num::ParseIntError; use thiserror::Error; /// A single token of the input stream; used to help the parsing go down @@ -26,6 +25,12 @@ use thiserror::Error; /// trait, you should get back the exact same token. #[derive(Logos, Clone, Debug, PartialEq, Eq)] pub enum Token { + // we're actually just going to skip whitespace, though + #[regex(r"[ \t\r\n\f]+", logos::skip)] + // this is an extremely simple version of comments, just line + // comments. More complicated /* */ comments can be harder to + // implement, and didn't seem worth it at the time. + #[regex(r"//.*", logos::skip)] // Our first set of tokens are simple characters that we're // going to use to structure NGR programs. #[token("=")] @@ -87,18 +92,6 @@ pub enum Token { // letter, too. #[regex(r"[a-z][a-zA-Z0-9_]*", |v| ArcIntern::new(v.slice().to_string()))] Variable(ArcIntern), - - // the next token will be an error token - #[error] - // we're actually just going to skip whitespace, though - #[regex(r"[ \t\r\n\f]+", logos::skip)] - // this is an extremely simple version of comments, just line - // comments. More complicated /* */ comments can be harder to - // implement, and didn't seem worth it at the time. - #[regex(r"//.*", logos::skip)] - /// This token represents that some core error happened in lexing; - /// possibly that something didn't match anything at all. - Error, } impl fmt::Display for Token { @@ -137,7 +130,6 @@ impl fmt::Display for Token { ) } Token::Variable(s) => write!(f, "'{}'", s), - Token::Error => write!(f, ""), } } } @@ -171,11 +163,13 @@ pub enum ConstantType { I16 = 21, I32 = 22, I64 = 23, + Void = 255, } impl From for cranelift_codegen::ir::Type { fn from(value: ConstantType) -> Self { match value { + ConstantType::Void => cranelift_codegen::ir::types::I64, ConstantType::I8 | ConstantType::U8 => cranelift_codegen::ir::types::I8, ConstantType::I16 | ConstantType::U16 => cranelift_codegen::ir::types::I16, ConstantType::I32 | ConstantType::U32 => cranelift_codegen::ir::types::I32, @@ -196,6 +190,7 @@ impl ConstantType { /// Return the set of types that can be safely casted into this type. pub fn safe_casts_to(self) -> Vec { match self { + ConstantType::Void => vec![ConstantType::Void], ConstantType::I8 => vec![ConstantType::I8], ConstantType::I16 => vec![ConstantType::I16, ConstantType::I8, ConstantType::U8], ConstantType::I32 => vec![ @@ -243,6 +238,7 @@ impl ConstantType { /// Return the name of the given type, as a string pub fn name(&self) -> String { match self { + ConstantType::Void => "void".to_string(), ConstantType::I8 => "i8".to_string(), ConstantType::I16 => "i16".to_string(), ConstantType::I32 => "i32".to_string(), @@ -286,7 +282,7 @@ impl TryFrom for ConstantType { fn parse_number( base: Option, value: &Lexer, -) -> Result<(Option, Option, u64), ParseIntError> { +) -> Result<(Option, Option, u64), ()> { let (radix, strval) = match base { None => (10, value.slice()), Some(radix) => (radix, &value.slice()[2..]), @@ -312,13 +308,14 @@ fn parse_number( (None, strval) }; - let intval = u64::from_str_radix(strval, radix as u32)?; + let intval = u64::from_str_radix(strval, radix as u32).map_err(|_| ())?; Ok((base, declared_type, intval)) } fn display_optional_type(otype: &Option) -> &'static str { match otype { None => "", + Some(ConstantType::Void) => "void", Some(ConstantType::I8) => "i8", Some(ConstantType::I16) => "i16", Some(ConstantType::I32) => "i32", @@ -333,18 +330,18 @@ fn display_optional_type(otype: &Option) -> &'static str { #[test] fn lex_numbers() { let mut lex0 = Token::lexer("12 0b1100 0o14 0d12 0xc 12u8 0xci64// 9"); - assert_eq!(lex0.next(), Some(Token::Number((None, None, 12)))); - assert_eq!(lex0.next(), Some(Token::Number((Some(2), None, 12)))); - assert_eq!(lex0.next(), Some(Token::Number((Some(8), None, 12)))); - assert_eq!(lex0.next(), Some(Token::Number((Some(10), None, 12)))); - assert_eq!(lex0.next(), Some(Token::Number((Some(16), None, 12)))); + assert_eq!(lex0.next(), Some(Ok(Token::Number((None, None, 12))))); + assert_eq!(lex0.next(), Some(Ok(Token::Number((Some(2), None, 12))))); + assert_eq!(lex0.next(), Some(Ok(Token::Number((Some(8), None, 12))))); + assert_eq!(lex0.next(), Some(Ok(Token::Number((Some(10), None, 12))))); + assert_eq!(lex0.next(), Some(Ok(Token::Number((Some(16), None, 12))))); assert_eq!( lex0.next(), - Some(Token::Number((None, Some(ConstantType::U8), 12))) + Some(Ok(Token::Number((None, Some(ConstantType::U8), 12)))) ); assert_eq!( lex0.next(), - Some(Token::Number((Some(16), Some(ConstantType::I64), 12))) + Some(Ok(Token::Number((Some(16), Some(ConstantType::I64), 12)))) ); assert_eq!(lex0.next(), None); } @@ -352,46 +349,52 @@ fn lex_numbers() { #[test] fn lex_symbols() { let mut lex0 = Token::lexer("x + \t y * \n z // rest"); - assert_eq!(lex0.next(), Some(Token::var("x"))); - assert_eq!(lex0.next(), Some(Token::Operator('+'))); - assert_eq!(lex0.next(), Some(Token::var("y"))); - assert_eq!(lex0.next(), Some(Token::Operator('*'))); - assert_eq!(lex0.next(), Some(Token::var("z"))); + assert_eq!(lex0.next(), Some(Ok(Token::var("x")))); + assert_eq!(lex0.next(), Some(Ok(Token::Operator('+')))); + assert_eq!(lex0.next(), Some(Ok(Token::var("y")))); + assert_eq!(lex0.next(), Some(Ok(Token::Operator('*')))); + assert_eq!(lex0.next(), Some(Ok(Token::var("z")))); assert_eq!(lex0.next(), None); } #[test] fn lexer_spans() { let mut lex0 = Token::lexer("y = x + 1//foo").spanned(); - assert_eq!(lex0.next(), Some((Token::var("y"), 0..1))); - assert_eq!(lex0.next(), Some((Token::Equals, 2..3))); - assert_eq!(lex0.next(), Some((Token::var("x"), 4..5))); - assert_eq!(lex0.next(), Some((Token::Operator('+'), 6..7))); - assert_eq!(lex0.next(), Some((Token::Number((None, None, 1)), 8..9))); + assert_eq!(lex0.next(), Some((Ok(Token::var("y")), 0..1))); + assert_eq!(lex0.next(), Some((Ok(Token::Equals), 2..3))); + assert_eq!(lex0.next(), Some((Ok(Token::var("x")), 4..5))); + assert_eq!(lex0.next(), Some((Ok(Token::Operator('+')), 6..7))); + assert_eq!( + lex0.next(), + Some((Ok(Token::Number((None, None, 1))), 8..9)) + ); assert_eq!(lex0.next(), None); } #[test] fn further_spans() { let mut lex0 = Token::lexer("x = 2i64 + 2i64;\ny = -x;\nprint y;").spanned(); - assert_eq!(lex0.next(), Some((Token::var("x"), 0..1))); - assert_eq!(lex0.next(), Some((Token::Equals, 2..3))); + assert_eq!(lex0.next(), Some((Ok(Token::var("x")), 0..1))); + assert_eq!(lex0.next(), Some((Ok(Token::Equals), 2..3))); assert_eq!( lex0.next(), - Some((Token::Number((None, Some(ConstantType::I64), 2)), 4..8)) + Some((Ok(Token::Number((None, Some(ConstantType::I64), 2))), 4..8)) ); - assert_eq!(lex0.next(), Some((Token::Operator('+'), 9..10))); + assert_eq!(lex0.next(), Some((Ok(Token::Operator('+')), 9..10))); assert_eq!( lex0.next(), - Some((Token::Number((None, Some(ConstantType::I64), 2)), 11..15)) + Some(( + Ok(Token::Number((None, Some(ConstantType::I64), 2))), + 11..15 + )) ); - assert_eq!(lex0.next(), Some((Token::Semi, 15..16))); - assert_eq!(lex0.next(), Some((Token::var("y"), 17..18))); - assert_eq!(lex0.next(), Some((Token::Equals, 19..20))); - assert_eq!(lex0.next(), Some((Token::Operator('-'), 21..22))); - assert_eq!(lex0.next(), Some((Token::var("x"), 22..23))); - assert_eq!(lex0.next(), Some((Token::Semi, 23..24))); - assert_eq!(lex0.next(), Some((Token::Print, 25..30))); - assert_eq!(lex0.next(), Some((Token::var("y"), 31..32))); - assert_eq!(lex0.next(), Some((Token::Semi, 32..33))); + assert_eq!(lex0.next(), Some((Ok(Token::Semi), 15..16))); + assert_eq!(lex0.next(), Some((Ok(Token::var("y")), 17..18))); + assert_eq!(lex0.next(), Some((Ok(Token::Equals), 19..20))); + assert_eq!(lex0.next(), Some((Ok(Token::Operator('-')), 21..22))); + assert_eq!(lex0.next(), Some((Ok(Token::var("x")), 22..23))); + assert_eq!(lex0.next(), Some((Ok(Token::Semi), 23..24))); + assert_eq!(lex0.next(), Some((Ok(Token::Print), 25..30))); + assert_eq!(lex0.next(), Some((Ok(Token::var("y")), 31..32))); + assert_eq!(lex0.next(), Some((Ok(Token::Semi), 32..33))); } diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 5cfa210..3e968b3 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -129,6 +129,7 @@ fn convert_statement( syntax::Statement::Binding(loc, name, expr) => { let (expr, ty) = convert_expression(expr, constraint_db, renames, bindings); let final_name = finalize_name(bindings, renames, name); + bindings.insert(final_name.clone(), ty.clone()); ir::Expression::Bind(loc, final_name, ty, Box::new(expr)) } @@ -168,6 +169,10 @@ fn convert_expression( )); (newval, newtype) } + Some(ConstantType::Void) => ( + ir::Value::Void, + ir::TypeOrVar::Primitive(PrimitiveType::Void), + ), Some(ConstantType::U8) => ( ir::Value::U8(base, value as u8), ir::TypeOrVar::Primitive(PrimitiveType::U8), diff --git a/src/type_infer/finalize.rs b/src/type_infer/finalize.rs index 59ff329..d681c34 100644 --- a/src/type_infer/finalize.rs +++ b/src/type_infer/finalize.rs @@ -174,6 +174,11 @@ fn finalize_val_or_ref( assert!(matches!(new_type, Type::Primitive(PrimitiveType::I64))); ValueOrRef::Value(loc, new_type, Value::I64(base, value)) } + + Value::Void => { + assert!(matches!(new_type, Type::Primitive(PrimitiveType::Void))); + ValueOrRef::Value(loc, new_type, Value::Void) + } } } } diff --git a/src/util/scoped_map.rs b/src/util/scoped_map.rs index 34e968b..83e54d4 100644 --- a/src/util/scoped_map.rs +++ b/src/util/scoped_map.rs @@ -1,4 +1,6 @@ -use std::{borrow::Borrow, collections::HashMap, hash::Hash}; +use std::borrow::Borrow; +use std::collections::HashMap; +use std::hash::Hash; /// A version of [`std::collections::HashMap`] with a built-in notion of scope. #[derive(Clone)] @@ -105,4 +107,28 @@ impl ScopedMap { ScopedMap { scopes } } + + /// Returns true if this map is completely empty, at every level of + /// scope. + pub fn is_empty(&self) -> bool { + self.scopes.iter().all(|x| x.is_empty()) + } +} + +impl ScopedMap { + /// Returns the set of all variables bound at this time, with shadowed + /// variables hidden. + pub fn bindings(&self) -> HashMap { + let mut result = HashMap::new(); + + for scope in self.scopes.iter().rev() { + for (key, value) in scope.iter() { + if !result.contains_key(key) { + result.insert(key.clone(), value.clone()); + } + } + } + + result + } } -- 2.53.0 From 5936f5a0d97a205aec0e92a67976bf0f3e4118b2 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Wed, 27 Dec 2023 10:27:18 -0800 Subject: [PATCH 10/59] jit works yay --- src/backend.rs | 30 +++++ src/backend/into_crane.rs | 268 ++++++-------------------------------- src/bin/ngrun.rs | 12 +- 3 files changed, 77 insertions(+), 233 deletions(-) diff --git a/src/backend.rs b/src/backend.rs index c6f217b..c6cc556 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -34,6 +34,7 @@ mod runtime; pub use self::error::BackendError; pub use self::runtime::{RuntimeFunctionError, RuntimeFunctions}; use crate::syntax::ConstantType; +use cranelift_codegen::entity::EntityRef; use cranelift_codegen::settings::Configurable; use cranelift_codegen::{isa, settings}; use cranelift_jit::{JITBuilder, JITModule}; @@ -62,6 +63,7 @@ pub struct Backend { defined_symbols: HashMap, (DataId, ConstantType)>, output_buffer: Option, platform: Triple, + next_variable: usize, } impl Backend { @@ -93,6 +95,7 @@ impl Backend { defined_symbols: HashMap::new(), output_buffer, platform: Triple::host(), + next_variable: 23, }) } @@ -132,6 +135,7 @@ impl Backend { defined_symbols: HashMap::new(), output_buffer: None, platform, + next_variable: 23, }) } @@ -187,6 +191,32 @@ impl Backend { Ok(id) } + pub fn string_reference(&mut self, string: &str) -> Result { + match self.defined_strings.get(string) { + Some(x) => Ok(*x), + None => self.define_string(string), + } + } + + /// Reset the local variable counter, because we're going to start a new + /// function. + pub fn reset_local_variable_tracker(&mut self) { + self.next_variable = 5; + } + + /// Declare a local variable with the given name and type. + /// + /// This variable should only be used to reference variables within the current + /// function. If you try to reference a variable from another function, random + /// things could happen; hopefully, Cranelift will yell at you, but there's a + /// small chance everything would work out and you'd end up referencing something + /// unexpected. + pub fn generate_local(&mut self) -> cranelift_frontend::Variable { + let var = cranelift_frontend::Variable::new(self.next_variable); + self.next_variable += 1; + var + } + /// Get a pointer to the output buffer for `print`ing, or `null`. /// /// As suggested, returns `null` in the case where the user has not provided an diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index 1b409f6..960cd55 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -18,18 +18,25 @@ use crate::backend::Backend; /// from a variable to "what to do if you want to reference this variable", which is /// agnostic about whether the variable is local, global, an argument, etc. Since /// the type of that function is a little bit annoying, we summarize it here. -struct ReferenceBuilder { - ir_type: ConstantType, - cranelift_type: cranelift_codegen::ir::Type, - local_data: GlobalValue, +enum ReferenceBuilder { + Global(ConstantType, GlobalValue), + Local(ConstantType, cranelift_frontend::Variable), } impl ReferenceBuilder { fn refer_to(&self, builder: &mut FunctionBuilder) -> (entities::Value, ConstantType) { - let value = builder - .ins() - .symbol_value(self.cranelift_type, self.local_data); - (value, self.ir_type) + match self { + ReferenceBuilder::Global(ty, gv) => { + let cranelift_type = ir::Type::from(*ty); + let value = builder.ins().symbol_value(cranelift_type, *gv); + (value, *ty) + } + + ReferenceBuilder::Local(ty, var) => { + let value = builder.use_var(*var); + (value, *ty) + } + } } } @@ -114,6 +121,10 @@ impl Backend { call_conv: CallConv::triple_default(&self.platform), }; + // reset the next variable counter. this value shouldn't matter; hopefully + // we won't be using close to 2^32 variables! + self.reset_local_variable_tracker(); + // this generates the handle for the function that we'll eventually want to // return to the user. For now, we declare all functions defined by this // function as public/global/exported, although we may want to reconsider @@ -143,15 +154,7 @@ impl Backend { // elsewhere in the program. for (name, (data_id, ty)) in self.defined_symbols.iter() { let local_data = self.module.declare_data_in_func(*data_id, &mut ctx.func); - let cranelift_type = ir::Type::from(*ty); - variables.insert( - name.clone(), - ReferenceBuilder { - cranelift_type, - local_data, - ir_type: *ty, - }, - ); + variables.insert(name.clone(), ReferenceBuilder::Global(*ty, local_data)); } // Once we have these, we're going to actually push a level of scope and @@ -177,7 +180,11 @@ impl Backend { // the equivalent of Rust's `return;`). We then seal the block (which lets Cranelift // know that the block is done), and then finalize the function (which lets Cranelift // know we're done with the function). - builder.ins().return_(&[value]); + if return_type == Type::Primitive(PrimitiveType::Void) { + builder.ins().return_(&[]); + } else { + builder.ins().return_(&[value]); + }; builder.seal_block(main_block); builder.finalize(); @@ -320,13 +327,10 @@ impl Backend { let buffer_ptr = builder.ins().iconst(types::I64, buffer_ptr as i64); // Get a reference to the string we want to print. - let string_data_id = self - .defined_strings - .get(var.as_ref()) - .ok_or_else(|| BackendError::UnknownString(var.clone()))?; + let string_data_id = self.string_reference(var.as_ref())?; let local_name_ref = self .module - .declare_data_in_func(*string_data_id, builder.func); + .declare_data_in_func(string_data_id, builder.func); let name_ptr = builder.ins().symbol_value(types::I64, local_name_ref); // Look up the value for the variable. Because this might be a @@ -360,7 +364,16 @@ impl Backend { Ok((builder.ins().iconst(types::I8, 0), ConstantType::I8)) } - Expression::Bind(_, _, _, _) => unimplemented!(), + Expression::Bind(_, name, _, expr) => { + let (value, value_type) = self.compile_expression(*expr, variables, builder)?; + let ir_type = ir::Type::from(value_type); + let variable = self.generate_local(); + + builder.declare_var(variable, ir_type); + builder.def_var(variable, value); + variables.insert(name, ReferenceBuilder::Local(value_type, variable)); + Ok((builder.ins().iconst(types::I8, 0), ConstantType::I8)) + } } } @@ -409,211 +422,4 @@ impl Backend { }, } } - - // Compiling a function is just compiling each of the statements in order. - // At the moment, we do the pattern match for statements here, and then - // directly compile the statements. If/when we add more statement forms, - // this is likely to become more cumbersome, and we'll want to separate - // these off. But for now, given the amount of tables we keep around to track - // state, it's easier to just include them. - // for item in program.items.drain(..) { - // match item { - // TopLevel::Function(_, _, _) => unimplemented!(), - // - // // Print statements are fairly easy to compile: we just lookup the - // // output buffer, the address of the string to print, and the value - // // of whatever variable we're printing. Then we just call print. - // TopLevel::Statement(Statement::Print(ann, t, var)) => { - // // Get the output buffer (or null) from our general compilation context. - // let buffer_ptr = self.output_buffer_ptr(); - // let buffer_ptr = builder.ins().iconst(types::I64, buffer_ptr as i64); - // - // // Get a reference to the string we want to print. - // let local_name_ref = string_table.get(&var).unwrap(); - // let name_ptr = builder.ins().symbol_value(types::I64, *local_name_ref); - // - // // Look up the value for the variable. Because this might be a - // // global variable (and that requires special logic), we just turn - // // this into an `Expression` and re-use the logic in that implementation. - // let (val, vtype) = ValueOrRef::Ref(ann, t, var).into_crane( - // &mut builder, - // &variable_table, - // &pre_defined_symbols, - // )?; - // - // let vtype_repr = builder.ins().iconst(types::I64, vtype as i64); - // - // let casted_val = match vtype { - // ConstantType::U64 | ConstantType::I64 => val, - // ConstantType::I8 | ConstantType::I16 | ConstantType::I32 => { - // builder.ins().sextend(types::I64, val) - // } - // ConstantType::U8 | ConstantType::U16 | ConstantType::U32 => { - // builder.ins().uextend(types::I64, val) - // } - // }; - // - // // Finally, we can generate the call to print. - // builder.ins().call( - // print_func_ref, - // &[buffer_ptr, name_ptr, vtype_repr, casted_val], - // ); - // } - // - // // Variable binding is a little more con - // TopLevel::Statement(Statement::Binding(_, var_name, _, value)) => { - // // Kick off to the `Expression` implementation to see what value we're going - // // to bind to this variable. - // let (val, etype) = - // value.into_crane(&mut builder, &variable_table, &pre_defined_symbols)?; - // - // // Now the question is: is this a local variable, or a global one? - // if let Some((global_id, ctype)) = pre_defined_symbols.get(var_name.as_str()) { - // // It's a global variable! In this case, we assume that someone has already - // // dedicated some space in memory to store this value. We look this location - // // up, and then tell Cranelift to store the value there. - // assert_eq!(etype, *ctype); - // let val_ptr = builder - // .ins() - // .symbol_value(ir::Type::from(*ctype), *global_id); - // builder.ins().store(MemFlags::new(), val, val_ptr, 0); - // } else { - // // It's a local variable! In this case, we need to allocate a new Cranelift - // // `Variable` for this variable, which we do using our `next_var_num` counter. - // // (While we're doing this, we also increment `next_var_num`, so that we get - // // a fresh `Variable` next time. This is one of those very narrow cases in which - // // I wish Rust had an increment expression.) - // let var = Variable::new(next_var_num); - // next_var_num += 1; - // - // // We can add the variable directly to our local variable map; it's `Copy`. - // variable_table.insert(var_name, (var, etype)); - // - // // Now we tell Cranelift about our new variable! - // builder.declare_var(var, ir::Type::from(etype)); - // builder.def_var(var, val); - // } - // } - // } - // } - - // Build the string table for use in referencing strings later. - // - // This function is slightly smart, in that it only puts strings in the table that - // are used by the `Program`. (Thanks to `Progam::strings()`!) If the strings have - // been declared globally, via `Backend::define_string()`, we will re-use that data. - // Otherwise, this will define the string for you. - // fn build_string_table( - // &mut self, - // func: &mut Function, - // program: &Expression, - // ) -> Result { - // let mut string_table = HashMap::new(); - // - // for interned_value in program.strings().drain() { - // let global_id = match self.defined_strings.get(interned_value.as_str()) { - // Some(x) => *x, - // None => self.define_string(interned_value.as_str())?, - // }; - // let local_data = self.module.declare_data_in_func(global_id, func); - // string_table.insert(interned_value, local_data); - // } - // - // Ok(string_table) - // } } - -//impl Expression { -// fn into_crane( -// self, -// builder: &mut FunctionBuilder, -// local_variables: &HashMap, (Variable, ConstantType)>, -// global_variables: &HashMap, -// ) -> Result<(entities::Value, ConstantType), BackendError> { -// match self { -// Expression::Atomic(x) => x.into_crane(builder, local_variables, global_variables), -// -// Expression::Cast(_, target_type, expr) => { -// let (val, val_type) = -// expr.into_crane(builder, local_variables, global_variables)?; -// -// match (val_type, &target_type) { -// } -// } -// -// Expression::Primitive(_, _, prim, mut vals) => { -// } -// } -// } -//} -// -//// Just to avoid duplication, this just leverages the `From` trait implementation -//// for `ValueOrRef` to compile this via the `Expression` logic, above. -//impl ValueOrRef { -// fn into_crane( -// self, -// builder: &mut FunctionBuilder, -// local_variables: &HashMap, (Variable, ConstantType)>, -// global_variables: &HashMap, -// ) -> Result<(entities::Value, ConstantType), BackendError> { -// match self { -// // Values are pretty straightforward to compile, mostly because we only -// // have one type of variable, and it's an integer type. -// ValueOrRef::Value(_, _, val) => match val { -// Value::I8(_, v) => { -// Ok((builder.ins().iconst(types::I8, v as i64), ConstantType::I8)) -// } -// Value::I16(_, v) => Ok(( -// builder.ins().iconst(types::I16, v as i64), -// ConstantType::I16, -// )), -// Value::I32(_, v) => Ok(( -// builder.ins().iconst(types::I32, v as i64), -// ConstantType::I32, -// )), -// Value::I64(_, v) => Ok((builder.ins().iconst(types::I64, v), ConstantType::I64)), -// Value::U8(_, v) => { -// Ok((builder.ins().iconst(types::I8, v as i64), ConstantType::U8)) -// } -// Value::U16(_, v) => Ok(( -// builder.ins().iconst(types::I16, v as i64), -// ConstantType::U16, -// )), -// Value::U32(_, v) => Ok(( -// builder.ins().iconst(types::I32, v as i64), -// ConstantType::U32, -// )), -// Value::U64(_, v) => Ok(( -// builder.ins().iconst(types::I64, v as i64), -// ConstantType::U64, -// )), -// }, -// -// ValueOrRef::Ref(_, _, name) => { -// // first we see if this is a local variable (which is nicer, from an -// // optimization point of view.) -// if let Some((local_var, etype)) = local_variables.get(&name) { -// return Ok((builder.use_var(*local_var), *etype)); -// } -// -// // then we check to see if this is a global reference, which requires us to -// // first lookup where the value is stored, and then load it. -// if let Some((global_var, etype)) = global_variables.get(name.as_ref()) { -// let cranelift_type = ir::Type::from(*etype); -// let val_ptr = builder.ins().symbol_value(cranelift_type, *global_var); -// return Ok(( -// builder -// .ins() -// .load(cranelift_type, MemFlags::new(), val_ptr, 0), -// *etype, -// )); -// } -// -// // this should never happen, because we should have made sure that there are -// // no unbound variables a long time before this. but still ... -// Err(BackendError::VariableLookupFailure(name)) -// } -// } -// } -//} -// diff --git a/src/bin/ngrun.rs b/src/bin/ngrun.rs index 9230f63..2296a06 100644 --- a/src/bin/ngrun.rs +++ b/src/bin/ngrun.rs @@ -1,5 +1,6 @@ use clap::Parser; use codespan_reporting::files::SimpleFiles; +use ngr::backend::Backend; use ngr::eval::Value; use ngr::syntax; use ngr::type_infer::TypeInferenceResult; @@ -70,5 +71,12 @@ fn main() -> Result<(), anyhow::Error> { return Ok(()); } - unimplemented!(); -} \ No newline at end of file + let mut backend = Backend::jit(None)?; + let function_id = backend.compile_program("gogogo", ir)?; + backend.module.finalize_definitions()?; + let compiled_bytes = backend.bytes(function_id); + let compiled_function = unsafe { std::mem::transmute::<_, fn() -> ()>(compiled_bytes) }; + compiled_function(); + + Ok(()) +} -- 2.53.0 From 7101b62efb1946b9ad260a30b2c9e94d67549844 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Wed, 27 Dec 2023 10:58:05 -0800 Subject: [PATCH 11/59] deal with unknown types at the syntax phase --- src/eval/primop.rs | 24 ++++++++++++++++++++++++ src/eval/primtype.rs | 2 ++ src/eval/value.rs | 16 ++++++++++++++++ src/syntax/eval.rs | 7 ++++++- 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/eval/primop.rs b/src/eval/primop.rs index aa9ffef..464562b 100644 --- a/src/eval/primop.rs +++ b/src/eval/primop.rs @@ -106,6 +106,7 @@ impl Value { match left { Value::I8(x) => match right { Value::I8(y) => run_op!(operation, x, *y), + Value::Number(y) => run_op!(operation, x, *y as i8), _ => Err(PrimOpError::TypeMismatch( operation.to_string(), left.clone(), @@ -114,6 +115,7 @@ impl Value { }, Value::I16(x) => match right { Value::I16(y) => run_op!(operation, x, *y), + Value::Number(y) => run_op!(operation, x, *y as i16), _ => Err(PrimOpError::TypeMismatch( operation.to_string(), left.clone(), @@ -122,6 +124,7 @@ impl Value { }, Value::I32(x) => match right { Value::I32(y) => run_op!(operation, x, *y), + Value::Number(y) => run_op!(operation, x, *y as i32), _ => Err(PrimOpError::TypeMismatch( operation.to_string(), left.clone(), @@ -130,6 +133,7 @@ impl Value { }, Value::I64(x) => match right { Value::I64(y) => run_op!(operation, x, *y), + Value::Number(y) => run_op!(operation, x, *y as i64), _ => Err(PrimOpError::TypeMismatch( operation.to_string(), left.clone(), @@ -138,6 +142,7 @@ impl Value { }, Value::U8(x) => match right { Value::U8(y) => run_op!(operation, x, *y), + Value::Number(y) => run_op!(operation, x, *y as u8), _ => Err(PrimOpError::TypeMismatch( operation.to_string(), left.clone(), @@ -146,6 +151,7 @@ impl Value { }, Value::U16(x) => match right { Value::U16(y) => run_op!(operation, x, *y), + Value::Number(y) => run_op!(operation, x, *y as u16), _ => Err(PrimOpError::TypeMismatch( operation.to_string(), left.clone(), @@ -154,6 +160,7 @@ impl Value { }, Value::U32(x) => match right { Value::U32(y) => run_op!(operation, x, *y), + Value::Number(y) => run_op!(operation, x, *y as u32), _ => Err(PrimOpError::TypeMismatch( operation.to_string(), left.clone(), @@ -162,12 +169,29 @@ impl Value { }, Value::U64(x) => match right { Value::U64(y) => run_op!(operation, x, *y), + Value::Number(y) => run_op!(operation, x, *y), _ => Err(PrimOpError::TypeMismatch( operation.to_string(), left.clone(), right.clone(), )), }, + Value::Number(x) => match right { + Value::Number(y) => run_op!(operation, x, *y), + Value::U8(y) => run_op!(operation, (*x as u8), *y), + Value::U16(y) => run_op!(operation, (*x as u16), *y), + Value::U32(y) => run_op!(operation, (*x as u32), *y), + Value::U64(y) => run_op!(operation, x, *y), + Value::I8(y) => run_op!(operation, (*x as i8), *y), + Value::I16(y) => run_op!(operation, (*x as i16), *y), + Value::I32(y) => run_op!(operation, (*x as i32), *y), + Value::I64(y) => run_op!(operation, (*x as i64), *y), + _ => Err(PrimOpError::TypeMismatch( + operation.to_string(), + left.clone(), + right.clone(), + )), + } Value::Closure(_, _, _, _) | Value::Void => { Err(PrimOpError::BadTypeFor(operation.to_string(), left.clone())) } diff --git a/src/eval/primtype.rs b/src/eval/primtype.rs index 3ed2b1c..dc42d1b 100644 --- a/src/eval/primtype.rs +++ b/src/eval/primtype.rs @@ -53,6 +53,8 @@ impl<'a, IR> TryFrom<&'a Value> for PrimitiveType { Value::U16(_) => Ok(PrimitiveType::U16), Value::U32(_) => Ok(PrimitiveType::U32), Value::U64(_) => Ok(PrimitiveType::U64), + // not sure this is the right call + Value::Number(_) => Ok(PrimitiveType::U64), Value::Closure(name, _, _, _) => Err(ValuePrimitiveTypeError::CannotConvertFunction( name.as_ref().map(|x| (**x).clone()), )), diff --git a/src/eval/value.rs b/src/eval/value.rs index c31d1ed..a49319b 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -18,6 +18,8 @@ pub enum Value { U16(u16), U32(u32), U64(u64), + // a number of unknown type + Number(u64), Closure( Option>, ScopedMap, Value>, @@ -43,6 +45,7 @@ impl Value { Value::I16(x) => Value::I16(*x), Value::I32(x) => Value::I32(*x), Value::I64(x) => Value::I64(*x), + Value::Number(x) => Value::Number(*x), Value::Closure(name, env, args, _) => { let new_env = env.clone().map_values(|x| x.strip()); Value::Closure(name.clone(), new_env, args.clone(), ()) @@ -62,6 +65,7 @@ fn format_value(value: &Value, f: &mut fmt::Formatter<'_>) -> fmt::Resul Value::U16(x) => write!(f, "{}u16", x), Value::U32(x) => write!(f, "{}u32", x), Value::U64(x) => write!(f, "{}u64", x), + Value::Number(x) => write!(f, "{}", x), Value::Closure(Some(name), _, _, _) => write!(f, "", name), Value::Closure(None, _, _, _) => write!(f, ""), } @@ -115,6 +119,18 @@ impl PartialEq> for Value { Value::U64(y) => x == y, _ => false, }, + Value::Number(x) => match other { + Value::I8(y) => (*x as i8) == *y, + Value::I16(y) => (*x as i16) == *y, + Value::I32(y) => (*x as i32) == *y, + Value::I64(y) => (*x as i64) == *y, + Value::U8(y) => (*x as u8) == *y, + Value::U16(y) => (*x as u16) == *y, + Value::U32(y) => (*x as u32) == *y, + Value::U64(y) => x == y, + Value::Number(y) => x == y, + _ => false, + } Value::Closure(_, _, _, _) => false, } } diff --git a/src/syntax/eval.rs b/src/syntax/eval.rs index 5813702..6f96102 100644 --- a/src/syntax/eval.rs +++ b/src/syntax/eval.rs @@ -45,6 +45,11 @@ impl Program { let value = env .get(&name.clone().intern()) .ok_or_else(|| EvalError::LookupFailed(loc.clone(), name.name.clone()))?; + let value = if let Value::Number(x) = value { + Value::U64(*x) + } else { + value.clone() + }; let line = format!("{} = {}\n", name, value); stdout.push_str(&line); last_result = Value::Void; @@ -64,7 +69,7 @@ impl Expression { match self { Expression::Value(_, v) => match v { super::Value::Number(_, ty, v) => match ty { - None => Ok(Value::U64(*v)), + None => Ok(Value::Number(*v)), // FIXME: make these types validate their input size Some(ConstantType::Void) => Ok(Value::Void), Some(ConstantType::I8) => Ok(Value::I8(*v as i8)), -- 2.53.0 From 53a9d081bbce73e80ea0f71a362f79b8d70d9571 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Wed, 27 Dec 2023 14:32:00 -0800 Subject: [PATCH 12/59] got some basics working! --- src/backend/error.rs | 23 +++++++++++++++++++ src/backend/eval.rs | 18 +++++++-------- src/backend/into_crane.rs | 33 ++++++++++++++++++++++++--- src/bin/ngrun.rs | 47 ++++++++++++++++++++++++++------------- src/eval/primop.rs | 2 +- src/eval/primtype.rs | 16 +++++++++++++ src/eval/value.rs | 2 +- src/ir/arbitrary.rs | 4 ++-- src/ir/ast.rs | 11 +++++++++ 9 files changed, 125 insertions(+), 31 deletions(-) diff --git a/src/backend/error.rs b/src/backend/error.rs index f7252e9..ad32cff 100644 --- a/src/backend/error.rs +++ b/src/backend/error.rs @@ -43,6 +43,11 @@ pub enum BackendError { InvalidTypeCast { from: PrimitiveType, to: Type }, #[error("Unknown string constant '{0}")] UnknownString(ArcIntern), + #[error("Compiler doesn't currently support function arguments")] + NoFunctionArguments { + function_name: String, + argument_name: String, + }, } impl From for Diagnostic { @@ -73,6 +78,13 @@ impl From for Diagnostic { ), BackendError::UnknownString(str) => Diagnostic::error() .with_message(format!("Unknown string found trying to compile: '{}'", str)), + BackendError::NoFunctionArguments { + function_name, + argument_name, + } => Diagnostic::error().with_message(format!( + "Function {} takes a function argument ({}), which is not supported", + function_name, argument_name + )), } } } @@ -128,6 +140,17 @@ impl PartialEq for BackendError { BackendError::UnknownString(b) => a == b, _ => false, }, + + BackendError::NoFunctionArguments { + function_name: f1, + argument_name: a1, + } => match other { + BackendError::NoFunctionArguments { + function_name: f2, + argument_name: a2, + } => f1 == f2 && a1 == a2, + _ => false, + }, } } } diff --git a/src/backend/eval.rs b/src/backend/eval.rs index 7768ef8..e3a77eb 100644 --- a/src/backend/eval.rs +++ b/src/backend/eval.rs @@ -206,15 +206,15 @@ proptest::proptest! { #[test] fn jit_backend(program in Program::arbitrary()) { use crate::eval::PrimOpError; -// use pretty::{DocAllocator, Pretty}; -// let allocator = pretty::BoxAllocator; -// allocator -// .text("---------------") -// .append(allocator.hardline()) -// .append(program.pretty(&allocator)) -// .1 -// .render_colored(70, pretty::termcolor::StandardStream::stdout(pretty::termcolor::ColorChoice::Auto)) -// .expect("rendering works"); + use pretty::{DocAllocator, Pretty}; + let allocator = pretty::BoxAllocator; + allocator + .text("---------------") + .append(allocator.hardline()) + .append(program.pretty(&allocator)) + .1 + .render_colored(70, pretty::termcolor::StandardStream::stdout(pretty::termcolor::ColorChoice::Auto)) + .expect("rendering works"); let basic_result = program.eval().map(|(_,x)| x); diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index 960cd55..7fb11fe 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -21,6 +21,7 @@ use crate::backend::Backend; enum ReferenceBuilder { Global(ConstantType, GlobalValue), Local(ConstantType, cranelift_frontend::Variable), + Argument(ConstantType, entities::Value), } impl ReferenceBuilder { @@ -36,6 +37,8 @@ impl ReferenceBuilder { let value = builder.use_var(*var); (value, *ty) } + + ReferenceBuilder::Argument(ty, val) => (*val, *ty), } } } @@ -172,6 +175,21 @@ impl Backend { // flow, we might add more blocks after this one. But, for now, we only have // the one block. let main_block = builder.create_block(); + // add the block parameters, which should be the function parameters + for (name, ty) in arguments.iter() { + let constant_type = ty + .try_into() + .map_err(|_| BackendError::NoFunctionArguments { + function_name: function_name.to_string(), + argument_name: name.to_string(), + })?; + let value = builder.append_block_param(main_block, ir::Type::from(constant_type)); + variables.insert( + name.clone(), + ReferenceBuilder::Argument(constant_type, value), + ); + } + builder.switch_to_block(main_block); let (value, _) = self.compile_expression(body, &mut variables, &mut builder)?; @@ -388,14 +406,23 @@ impl Backend { match valref { ValueOrRef::Value(_, _, val) => match val { Value::I8(_, v) => { - Ok((builder.ins().iconst(types::I8, v as i64), ConstantType::I8)) + // Cranelift does a funny thing where it checks you aren't using bits in the I64 + // we provide above the size of the type we provide. So, in this case, we can only + // set the low 8 bits of the i64. This restriction creates a bit of a problem when + // casting direction from i8 to i64, because Rust will (helpfully) sign-extend the + // negative number for us. Which sets the high bits, which makes Cranelift unhappy. + // So first we cast the i8 as u8, to get rid of the whole concept of sign extension, + // and *then* we cast to i64. + Ok((builder.ins().iconst(types::I8, v as u8 as i64), ConstantType::I8)) } Value::I16(_, v) => Ok(( - builder.ins().iconst(types::I16, v as i64), + // see above note for the "... as ... as" + builder.ins().iconst(types::I16, v as u16 as i64), ConstantType::I16, )), Value::I32(_, v) => Ok(( - builder.ins().iconst(types::I32, v as i64), + // see above note for the "... as ... as" + builder.ins().iconst(types::I32, v as u32 as i64), ConstantType::I32, )), Value::I64(_, v) => Ok((builder.ins().iconst(types::I64, v), ConstantType::I64)), diff --git a/src/bin/ngrun.rs b/src/bin/ngrun.rs index 2296a06..1d5ca43 100644 --- a/src/bin/ngrun.rs +++ b/src/bin/ngrun.rs @@ -33,19 +33,37 @@ fn print_result(result: (Value, String)) { println!("RESULT: {}", result.0); } -fn main() -> Result<(), anyhow::Error> { +fn jit(ir: ngr::ir::Program) -> Result { + let mut backend = Backend::jit(None)?; + let function_id = backend.compile_program("gogogo", ir)?; + backend.module.finalize_definitions()?; + let compiled_bytes = backend.bytes(function_id); + Ok(unsafe { std::mem::transmute::<_, fn() -> ()>(compiled_bytes) }) +} + +fn main() { let cli = CommandLineArguments::parse(); let mut file_database = SimpleFiles::new(); let mut console = StandardStream::stdout(pretty::termcolor::ColorChoice::Auto); let console_options = codespan_reporting::term::Config::default(); - let syntax = syntax::Program::parse_file(&mut file_database, cli.file.as_ref())?; + let syntax = syntax::Program::parse_file(&mut file_database, cli.file.as_ref()); let mut emit = |x| { let _ = codespan_reporting::term::emit(&mut console, &console_options, &file_database, &x); }; + let syntax = match syntax { + Ok(x) => x, + Err(e) => { + emit((&e).into()); + return; + } + }; if cli.interpreter == Interpreter::Syntax { - print_result(syntax.eval()?); - return Ok(()); + match syntax.eval() { + Err(e) => println!("Evaluation error: {}", e), + Ok(v) => print_result(v), + } + return; } let ir = match syntax.type_infer() { @@ -62,21 +80,20 @@ fn main() -> Result<(), anyhow::Error> { for error in errors { emit(error.into()); } - return Err(anyhow::anyhow!("failed to infer program types")); + return; } }; if cli.interpreter == Interpreter::IR { - print_result(ir.eval()?); - return Ok(()); + match ir.eval() { + Err(e) => println!("Evaluation error: {}", e), + Ok(v) => print_result(v), + } + return; } - let mut backend = Backend::jit(None)?; - let function_id = backend.compile_program("gogogo", ir)?; - backend.module.finalize_definitions()?; - let compiled_bytes = backend.bytes(function_id); - let compiled_function = unsafe { std::mem::transmute::<_, fn() -> ()>(compiled_bytes) }; - compiled_function(); - - Ok(()) + match jit(ir) { + Err(e) => emit(e.into()), + Ok(compiled_function) => compiled_function(), + } } diff --git a/src/eval/primop.rs b/src/eval/primop.rs index 464562b..322459f 100644 --- a/src/eval/primop.rs +++ b/src/eval/primop.rs @@ -191,7 +191,7 @@ impl Value { left.clone(), right.clone(), )), - } + }, Value::Closure(_, _, _, _) | Value::Void => { Err(PrimOpError::BadTypeFor(operation.to_string(), left.clone())) } diff --git a/src/eval/primtype.rs b/src/eval/primtype.rs index dc42d1b..0624ba2 100644 --- a/src/eval/primtype.rs +++ b/src/eval/primtype.rs @@ -78,6 +78,22 @@ impl From for PrimitiveType { } } +impl From for ConstantType { + fn from(value: PrimitiveType) -> Self { + match value { + PrimitiveType::Void => ConstantType::Void, + PrimitiveType::I8 => ConstantType::I8, + PrimitiveType::I16 => ConstantType::I16, + PrimitiveType::I32 => ConstantType::I32, + PrimitiveType::I64 => ConstantType::I64, + PrimitiveType::U8 => ConstantType::U8, + PrimitiveType::U16 => ConstantType::U16, + PrimitiveType::U32 => ConstantType::U32, + PrimitiveType::U64 => ConstantType::U64, + } + } +} + #[derive(thiserror::Error, Debug, Clone, PartialEq)] pub enum UnknownPrimType { #[error("Could not convert '{0}' into a primitive type")] diff --git a/src/eval/value.rs b/src/eval/value.rs index a49319b..e3b8230 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -130,7 +130,7 @@ impl PartialEq> for Value { Value::U64(y) => x == y, Value::Number(y) => x == y, _ => false, - } + }, Value::Closure(_, _, _, _) => false, } } diff --git a/src/ir/arbitrary.rs b/src/ir/arbitrary.rs index bf531a5..47aed0b 100644 --- a/src/ir/arbitrary.rs +++ b/src/ir/arbitrary.rs @@ -226,11 +226,11 @@ impl ValueTree for ProgramTree { } fn simplify(&mut self) -> bool { - unimplemented!() + false } fn complicate(&mut self) -> bool { - unimplemented!() + false } } diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 62737fd..b3d83d3 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -466,6 +466,17 @@ impl From for Type { } } +impl<'a> TryInto for &'a Type { + type Error = (); + + fn try_into(self) -> Result { + match self { + Type::Primitive(pt) => Ok((*pt).into()), + Type::Function(_, _) => Err(()), + } + } +} + #[derive(Clone, Debug, Eq, PartialEq)] pub enum TypeOrVar { Primitive(PrimitiveType), -- 2.53.0 From 7ebb31b42f12102250d61ea516d13dce58cd2dee Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Thu, 28 Dec 2023 20:57:03 -0800 Subject: [PATCH 13/59] checkpoint --- src/backend/eval.rs | 2 +- src/backend/into_crane.rs | 5 ++- src/eval/primtype.rs | 3 ++ src/ir/ast.rs | 6 +++- src/syntax/ast.rs | 5 +++ src/syntax/eval.rs | 67 +++++++++++++++++++++++++------------- src/syntax/parser.lalrpop | 6 ++-- src/syntax/pretty.rs | 19 +++++++++++ src/syntax/validate.rs | 18 +++++++++- src/type_infer/convert.rs | 24 ++++++++++++-- src/type_infer/finalize.rs | 20 ++++++++++++ src/type_infer/solve.rs | 22 +++++++++++++ 12 files changed, 166 insertions(+), 31 deletions(-) diff --git a/src/backend/eval.rs b/src/backend/eval.rs index e3a77eb..f169e6f 100644 --- a/src/backend/eval.rs +++ b/src/backend/eval.rs @@ -159,7 +159,7 @@ impl Backend { .arg(executable_path) .output()?; - if !output.stderr.is_empty() { + if !output.status.success() { return Err(EvalError::Linker( std::string::String::from_utf8_lossy(&output.stderr).to_string(), )); diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index 7fb11fe..8db56bc 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -413,7 +413,10 @@ impl Backend { // negative number for us. Which sets the high bits, which makes Cranelift unhappy. // So first we cast the i8 as u8, to get rid of the whole concept of sign extension, // and *then* we cast to i64. - Ok((builder.ins().iconst(types::I8, v as u8 as i64), ConstantType::I8)) + Ok(( + builder.ins().iconst(types::I8, v as u8 as i64), + ConstantType::I8, + )) } Value::I16(_, v) => Ok(( // see above note for the "... as ... as" diff --git a/src/eval/primtype.rs b/src/eval/primtype.rs index 0624ba2..fed57f3 100644 --- a/src/eval/primtype.rs +++ b/src/eval/primtype.rs @@ -113,6 +113,7 @@ impl FromStr for PrimitiveType { "u16" => Ok(PrimitiveType::U16), "u32" => Ok(PrimitiveType::U32), "u64" => Ok(PrimitiveType::U64), + "void" => Ok(PrimitiveType::Void), _ => Err(UnknownPrimType::UnknownPrimType(s.to_owned())), } } @@ -190,6 +191,8 @@ impl PrimitiveType { (PrimitiveType::I64, Value::I32(x)) => Ok(Value::I64(*x as i64)), (PrimitiveType::I64, Value::I64(x)) => Ok(Value::I64(*x)), + (PrimitiveType::Void, Value::Void) => Ok(Value::Void), + _ => Err(PrimOpError::UnsafeCast { from: PrimitiveType::try_from(source)?, to: *self, diff --git a/src/ir/ast.rs b/src/ir/ast.rs index b3d83d3..78c6998 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -228,9 +228,13 @@ where .text("print") .append(allocator.space()) .append(allocator.text(var.as_ref().to_string())), - Expression::Bind(_, var, _, expr) => allocator + Expression::Bind(_, var, ty, expr) => allocator .text(var.as_ref().to_string()) .append(allocator.space()) + .append(allocator.text(":")) + .append(allocator.space()) + .append(ty.pretty(allocator)) + .append(allocator.space()) .append(allocator.text("=")) .append(allocator.space()) .append(expr.pretty(allocator)), diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index b97851f..2ec0894 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -124,6 +124,7 @@ pub enum Expression { Reference(Location, String), Cast(Location, String, Box), Primitive(Location, String, Vec), + Block(Location, Vec), } impl PartialEq for Expression { @@ -145,6 +146,10 @@ impl PartialEq for Expression { Expression::Primitive(_, prim2, args2) => prim1 == prim2 && args1 == args2, _ => false, }, + Expression::Block(_, stmts1) => match other { + Expression::Block(_, stmts2) => stmts1 == stmts2, + _ => false, + }, } } } diff --git a/src/syntax/eval.rs b/src/syntax/eval.rs index 6f96102..6093633 100644 --- a/src/syntax/eval.rs +++ b/src/syntax/eval.rs @@ -35,25 +35,7 @@ impl Program { } } - TopLevel::Statement(Statement::Binding(_, name, value)) => { - let actual_value = value.eval(&env)?; - env.insert(name.clone().intern(), actual_value); - last_result = Value::Void; - } - - TopLevel::Statement(Statement::Print(loc, name)) => { - let value = env - .get(&name.clone().intern()) - .ok_or_else(|| EvalError::LookupFailed(loc.clone(), name.name.clone()))?; - let value = if let Value::Number(x) = value { - Value::U64(*x) - } else { - value.clone() - }; - let line = format!("{} = {}\n", name, value); - stdout.push_str(&line); - last_result = Value::Void; - } + TopLevel::Statement(stmt) => last_result = stmt.eval(&mut stdout, &mut env)?, } } @@ -61,10 +43,41 @@ impl Program { } } +impl Statement { + fn eval( + &self, + stdout: &mut String, + env: &mut ScopedMap, Value>, + ) -> Result, EvalError> { + match self { + Statement::Binding(_, name, value) => { + let actual_value = value.eval(stdout, env)?; + env.insert(name.clone().intern(), actual_value); + Ok(Value::Void) + } + + Statement::Print(loc, name) => { + let value = env + .get(&name.clone().intern()) + .ok_or_else(|| EvalError::LookupFailed(loc.clone(), name.name.clone()))?; + let value = if let Value::Number(x) = value { + Value::U64(*x) + } else { + value.clone() + }; + let line = format!("{} = {}\n", name, value); + stdout.push_str(&line); + Ok(Value::Void) + } + } + } +} + impl Expression { fn eval( &self, - env: &ScopedMap, Value>, + stdout: &mut String, + env: &mut ScopedMap, Value>, ) -> Result, EvalError> { match self { Expression::Value(_, v) => match v { @@ -90,7 +103,7 @@ impl Expression { Expression::Cast(_, target, expr) => { let target_type = PrimitiveType::from_str(target)?; - let value = expr.eval(env)?; + let value = expr.eval(stdout, env)?; Ok(target_type.safe_cast(&value)?) } @@ -99,11 +112,21 @@ impl Expression { for arg in args.iter() { // yay, recursion! makes this pretty straightforward - arg_values.push(arg.eval(env)?); + arg_values.push(arg.eval(stdout, env)?); } Ok(Value::calculate(op, arg_values)?) } + + Expression::Block(_, stmts) => { + let mut result = Value::Void; + + for stmt in stmts.iter() { + result = stmt.eval(stdout, env)?; + } + + Ok(result) + } } } } diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index 40987f3..8c66e63 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -87,11 +87,13 @@ OptionalName: Option = { } Arguments: Vec = { - => { + "," => { args.push(arg); args }, + => vec![arg], + => Vec::new(), } @@ -214,7 +216,7 @@ AtomicExpression: Expression = { // just a number "> => Expression::Value(Location::new(file_idx, l..end), Value::Number(n.0, n.1, n.2)), // this expression could actually be a block! - "{" "}" => unimplemented!(), + "{" "}" => Expression::Block(Location::new(file_idx, s..e), stmts), // finally, let people parenthesize expressions and get back to a // lower precedence "(" ")" => e, diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index 9bb0461..859f51f 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -105,6 +105,25 @@ where let comma_sepped_args = allocator.intersperse(args, CommaSep {}); call.append(comma_sepped_args.parens()) } + Expression::Block(_, stmts) => match stmts.split_last() { + None => allocator.text("()"), + Some((last, &[])) => last.pretty(allocator), + Some((last, start)) => { + let mut result = allocator.text("{").append(allocator.hardline()); + + for stmt in start.iter() { + result = result + .append(stmt.pretty(allocator)) + .append(allocator.text(";")) + .append(allocator.hardline()); + } + + result + .append(last.pretty(allocator)) + .append(allocator.hardline()) + .append(allocator.text("}")) + } + }, } } } diff --git a/src/syntax/validate.rs b/src/syntax/validate.rs index 46c471c..1503c65 100644 --- a/src/syntax/validate.rs +++ b/src/syntax/validate.rs @@ -180,7 +180,10 @@ impl Statement { } impl Expression { - fn validate(&self, variable_map: &ScopedMap) -> (Vec, Vec) { + fn validate( + &self, + variable_map: &mut ScopedMap, + ) -> (Vec, Vec) { match self { Expression::Value(_, _) => (vec![], vec![]), Expression::Reference(_, var) if variable_map.contains_key(var) => (vec![], vec![]), @@ -207,6 +210,19 @@ impl Expression { warnings.append(&mut warn); } + (errors, warnings) + } + Expression::Block(_, stmts) => { + let mut errors = vec![]; + let mut warnings = vec![]; + + for stmt in stmts.iter() { + let (mut local_errors, mut local_warnings) = stmt.validate(variable_map); + + errors.append(&mut local_errors); + warnings.append(&mut local_warnings); + } + (errors, warnings) } } diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 3e968b3..4a1800b 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -71,9 +71,10 @@ pub fn convert_top_level( args.iter().map(|x| ArcIntern::new(x.to_string())).collect(); assert_eq!(argtypes.len(), iargs.len()); let mut function_args = vec![]; - for (arg_name, arg_type) in iargs.iter().zip(argtypes) { + for ((arg_name, arg_type), orig_name) in iargs.iter().zip(argtypes).zip(args) { bindings.insert(arg_name.clone(), arg_type.clone()); - function_args.push((arg_name.clone(), arg_type)); + function_args.push((arg_name.clone(), arg_type.clone())); + constraint_db.push(Constraint::IsSomething(orig_name.location, arg_type)); } let (expr, ty) = convert_expression(expr, constraint_db, renames, bindings); @@ -150,7 +151,7 @@ fn convert_statement( fn convert_expression( expression: syntax::Expression, constraint_db: &mut Vec, - renames: &ScopedMap, ArcIntern>, + renames: &mut ScopedMap, ArcIntern>, bindings: &mut ScopedMap, ir::TypeOrVar>, ) -> (ir::Expression, ir::TypeOrVar) { match expression { @@ -282,6 +283,23 @@ fn convert_expression( ) } } + + syntax::Expression::Block(loc, stmts) => { + let mut ret_type = ir::TypeOrVar::Primitive(PrimitiveType::Void); + let mut exprs = vec![]; + + for statement in stmts { + let expr = convert_statement(statement, constraint_db, renames, bindings); + + ret_type = expr.type_of(); + exprs.push(expr); + } + + ( + ir::Expression::Block(loc, ret_type.clone(), exprs), + ret_type, + ) + } } } diff --git a/src/type_infer/finalize.rs b/src/type_infer/finalize.rs index d681c34..e2af988 100644 --- a/src/type_infer/finalize.rs +++ b/src/type_infer/finalize.rs @@ -6,6 +6,26 @@ pub fn finalize_program( mut program: Program, resolutions: &TypeResolutions, ) -> Program { + println!("RESOLUTIONS:"); + for (name, ty) in resolutions.iter() { + println!("{} => {}", name, ty); + } + println!("PROGRAM:"); + { + use pretty::{DocAllocator, Pretty}; + let allocator = pretty::BoxAllocator; + allocator + .text("---------------") + .append(allocator.hardline()) + .append(program.pretty(&allocator)) + .1 + .render_colored( + 70, + pretty::termcolor::StandardStream::stdout(pretty::termcolor::ColorChoice::Auto), + ) + .expect("rendering works"); + } + Program { items: program .items diff --git a/src/type_infer/solve.rs b/src/type_infer/solve.rs index f3ba8c8..34127de 100644 --- a/src/type_infer/solve.rs +++ b/src/type_infer/solve.rs @@ -25,6 +25,8 @@ pub enum Constraint { ConstantNumericType(Location, TypeOrVar), /// The two types should be equivalent Equivalent(Location, TypeOrVar, TypeOrVar), + /// The given type can be resolved to something + IsSomething(Location, TypeOrVar), } impl fmt::Display for Constraint { @@ -43,6 +45,7 @@ impl fmt::Display for Constraint { Constraint::NumericType(_, ty) => write!(f, "NUMERIC {}", ty), Constraint::ConstantNumericType(_, ty) => write!(f, "CONST_NUMERIC {}", ty), Constraint::Equivalent(_, ty, ty2) => write!(f, "EQUIVALENT {} => {}", ty, ty2), + Constraint::IsSomething(_, ty) => write!(f, "SOMETHING {}", ty), } } } @@ -213,6 +216,10 @@ impl From for Diagnostic { prim )) } + TypeInferenceError::CouldNotSolve(Constraint::IsSomething(loc, _)) => { + loc.labelled_error("could not infer type") + .with_message("Could not find *any* type information; is this an unused function argument?") + } } } } @@ -253,6 +260,11 @@ pub fn solve_constraints( let mut resolutions = HashMap::new(); let mut changed_something = true; + println!("CONSTRAINTS:"); + for constraint in constraint_db.iter() { + println!("{}", constraint); + } + // We want to run this inference endlessly, until either we have solved all of our // constraints. Internal to the loop, we have a check that will make sure that we // do (eventually) stop. @@ -275,6 +287,16 @@ pub fn solve_constraints( // Currently, all of our types are printable Constraint::Printable(_loc, _ty) => changed_something = true, + // If we're looking for a type to be something (anything!), and it's not a type + // variable, then yay, we've solved it. + Constraint::IsSomething(_, TypeOrVar::Function(_, _)) + | Constraint::IsSomething(_, TypeOrVar::Primitive(_)) => changed_something = true, + + // Otherwise, we'll keep looking for it. + Constraint::IsSomething(_, TypeOrVar::Variable(_, _)) => { + constraint_db.push(constraint); + } + // Case #1a: We have two primitive types. If they're equal, we've discharged this // constraint! We can just continue. If they're not equal, add an error and then // see what else we come up with. -- 2.53.0 From 4ba196d2a6798ebf336a4039e4e455de01643327 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Fri, 2 Feb 2024 10:31:13 -0800 Subject: [PATCH 14/59] jit works --- Cargo.toml | 22 ++-- examples/basic/generated0001.ngr | 3 + examples/basic/generated0002.ngr | 7 ++ examples/basic/generated0003.ngr | 4 + examples/basic/test1.ngr | 2 +- runtime/rts.c | 5 + src/backend.rs | 3 + src/backend/error.rs | 1 + src/backend/eval.rs | 59 ++++------- src/backend/into_crane.rs | 177 ++++++++++++++++++++++++------- src/backend/runtime.rs | 2 +- src/eval/primtype.rs | 7 ++ src/ir.rs | 1 + src/ir/arbitrary.rs | 21 +++- src/ir/ast.rs | 90 ++++++++++++++-- src/ir/top_level.rs | 45 ++++++++ src/syntax/ast.rs | 13 +++ src/syntax/tokens.rs | 1 + src/type_infer/convert.rs | 85 ++++++++++----- src/type_infer/finalize.rs | 2 +- src/type_infer/solve.rs | 112 ++++++++++--------- 21 files changed, 477 insertions(+), 185 deletions(-) create mode 100644 examples/basic/generated0001.ngr create mode 100644 examples/basic/generated0002.ngr create mode 100644 examples/basic/generated0003.ngr create mode 100644 src/ir/top_level.rs diff --git a/Cargo.toml b/Cargo.toml index 296f284..4e6b807 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,15 +9,15 @@ name = "ngr" path = "src/lib.rs" [dependencies] -clap = { version = "4.4.11", features = ["derive"] } +clap = { version = "4.4.18", features = ["derive"] } codespan = "0.11.1" codespan-reporting = "0.11.1" -cranelift-codegen = "0.103.0" -cranelift-jit = "0.103.0" -cranelift-frontend = "0.103.0" -cranelift-module = "0.103.0" -cranelift-native = "0.103.0" -cranelift-object = "0.103.0" +cranelift-codegen = "0.104.0" +cranelift-jit = "0.104.0" +cranelift-frontend = "0.104.0" +cranelift-module = "0.104.0" +cranelift-native = "0.104.0" +cranelift-object = "0.104.0" internment = { version = "0.7.4", default-features = false, features = ["arc"] } lalrpop-util = "0.20.0" lazy_static = "1.4.0" @@ -26,10 +26,10 @@ pretty = { version = "0.12.3", features = ["termcolor"] } proptest = "1.4.0" rand = "0.8.5" rustyline = "13.0.0" -target-lexicon = "0.12.12" -tempfile = "3.8.1" -thiserror = "1.0.52" -anyhow = "1.0.77" +target-lexicon = "0.12.13" +tempfile = "3.9.0" +thiserror = "1.0.56" +anyhow = "1.0.79" [build-dependencies] lalrpop = "0.20.0" diff --git a/examples/basic/generated0001.ngr b/examples/basic/generated0001.ngr new file mode 100644 index 0000000..ae46375 --- /dev/null +++ b/examples/basic/generated0001.ngr @@ -0,0 +1,3 @@ +x = 4u64; +function f(y) (x + y) +print x; \ No newline at end of file diff --git a/examples/basic/generated0002.ngr b/examples/basic/generated0002.ngr new file mode 100644 index 0000000..cc51a9e --- /dev/null +++ b/examples/basic/generated0002.ngr @@ -0,0 +1,7 @@ +b = -7662558304906888395i64; +z = 1030390794u32; +v = z; +q = z; +s = -2115098981i32; +t = s; +print t; \ No newline at end of file diff --git a/examples/basic/generated0003.ngr b/examples/basic/generated0003.ngr new file mode 100644 index 0000000..616bba6 --- /dev/null +++ b/examples/basic/generated0003.ngr @@ -0,0 +1,4 @@ +n = (49u8 + 155u8); +q = n; +function u (b) n + b +v = n; \ No newline at end of file diff --git a/examples/basic/test1.ngr b/examples/basic/test1.ngr index b8d66e1..decdcbd 100644 --- a/examples/basic/test1.ngr +++ b/examples/basic/test1.ngr @@ -1,4 +1,4 @@ x = 5; y = 4*x + 3; print x; -print y; +print y; \ No newline at end of file diff --git a/runtime/rts.c b/runtime/rts.c index a999bae..d8eba53 100644 --- a/runtime/rts.c +++ b/runtime/rts.c @@ -28,6 +28,11 @@ void print(char *_ignore, char *variable_name, int64_t vtype, int64_t value) { case /* I64 = */ 23: printf("%s = %" PRIi64 "i64\n", variable_name, value); break; + case /* void = */ 255: + printf("%s = \n", variable_name); + break; + default: + printf("%s = UNKNOWN VTYPE %d\n", variable_name, vtype); } } diff --git a/src/backend.rs b/src/backend.rs index c6cc556..b2fee29 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -60,6 +60,7 @@ pub struct Backend { data_ctx: DataDescription, runtime_functions: RuntimeFunctions, defined_strings: HashMap, + defined_functions: HashMap, FuncId>, defined_symbols: HashMap, (DataId, ConstantType)>, output_buffer: Option, platform: Triple, @@ -92,6 +93,7 @@ impl Backend { data_ctx: DataDescription::new(), runtime_functions, defined_strings: HashMap::new(), + defined_functions: HashMap::new(), defined_symbols: HashMap::new(), output_buffer, platform: Triple::host(), @@ -132,6 +134,7 @@ impl Backend { data_ctx: DataDescription::new(), runtime_functions, defined_strings: HashMap::new(), + defined_functions: HashMap::new(), defined_symbols: HashMap::new(), output_buffer: None, platform, diff --git a/src/backend/error.rs b/src/backend/error.rs index ad32cff..e5e2a1e 100644 --- a/src/backend/error.rs +++ b/src/backend/error.rs @@ -55,6 +55,7 @@ impl From for Diagnostic { match value { BackendError::Cranelift(me) => { Diagnostic::error().with_message(format!("Internal cranelift error: {}", me)) + .with_notes(vec![format!("{:?}", me)]) } BackendError::BuiltinError(me) => { Diagnostic::error().with_message(format!("Internal runtime function error: {}", me)) diff --git a/src/backend/eval.rs b/src/backend/eval.rs index f169e6f..3c639b1 100644 --- a/src/backend/eval.rs +++ b/src/backend/eval.rs @@ -26,34 +26,7 @@ impl Backend { /// of the built-in test systems.) pub fn eval(program: Program) -> Result>> { let mut jitter = Backend::jit(Some(String::new()))?; - let mut function_map = HashMap::new(); - let mut main_function_body = vec![]; - - for item in program.items { - match item { - TopLevel::Function(name, args, rettype, body) => { - let function_id = - jitter.compile_function(name.as_str(), args.as_slice(), rettype, body)?; - function_map.insert(name, function_id); - } - - TopLevel::Statement(stmt) => { - main_function_body.push(stmt); - } - } - } - - let main_function_body = Expression::Block( - Location::manufactured(), - Type::Primitive(crate::eval::PrimitiveType::Void), - main_function_body, - ); - let function_id = jitter.compile_function( - "___test_jit_eval___", - &[], - Type::Primitive(crate::eval::PrimitiveType::Void), - main_function_body, - )?; + let function_id = jitter.compile_program("___test_jit_eval___", program)?; jitter.module.finalize_definitions()?; let compiled_bytes = jitter.bytes(function_id); let compiled_function = unsafe { std::mem::transmute::<_, fn() -> ()>(compiled_bytes) }; @@ -89,8 +62,13 @@ impl Backend { for item in program.items { match item { TopLevel::Function(name, args, rettype, body) => { - let function_id = - backend.compile_function(name.as_str(), args.as_slice(), rettype, body)?; + let function_id = backend.compile_function( + &mut HashMap::new(), + name.as_str(), + args.as_slice(), + rettype, + body, + )?; function_map.insert(name, function_id); } @@ -111,6 +89,7 @@ impl Backend { let executable_path = my_directory.path().join("test_executable"); backend.compile_function( + &mut HashMap::new(), "gogogo", &[], Type::Primitive(crate::eval::PrimitiveType::Void), @@ -154,6 +133,7 @@ impl Backend { .join("runtime") .join("rts.c"), ) + .arg("-Wl,-ld_classic") .arg(object_file) .arg("-o") .arg(executable_path) @@ -206,16 +186,15 @@ proptest::proptest! { #[test] fn jit_backend(program in Program::arbitrary()) { use crate::eval::PrimOpError; - use pretty::{DocAllocator, Pretty}; - let allocator = pretty::BoxAllocator; - allocator - .text("---------------") - .append(allocator.hardline()) - .append(program.pretty(&allocator)) - .1 - .render_colored(70, pretty::termcolor::StandardStream::stdout(pretty::termcolor::ColorChoice::Auto)) - .expect("rendering works"); - + // use pretty::{DocAllocator, Pretty}; + // let allocator = pretty::BoxAllocator; + // allocator + // .text("---------------") + // .append(allocator.hardline()) + // .append(program.pretty(&allocator)) + // .1 + // .render_colored(70, pretty::termcolor::StandardStream::stdout(pretty::termcolor::ColorChoice::Auto)) + // .expect("rendering works"); let basic_result = program.eval().map(|(_,x)| x); diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index 8db56bc..81a9142 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -1,24 +1,23 @@ +use crate::backend::error::BackendError; +use crate::backend::Backend; use crate::eval::PrimitiveType; use crate::ir::{Expression, Primitive, Program, TopLevel, Type, Value, ValueOrRef, Variable}; use crate::syntax::{ConstantType, Location}; -use crate::util::scoped_map::ScopedMap; use cranelift_codegen::ir::{ - self, entities, types, AbiParam, Function, GlobalValue, InstBuilder, Signature, UserFuncName, + self, entities, types, AbiParam, Function, GlobalValue, InstBuilder, MemFlags, Signature, UserFuncName }; use cranelift_codegen::isa::CallConv; use cranelift_codegen::Context; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; -use cranelift_module::{FuncId, Linkage, Module}; +use cranelift_module::{DataDescription, FuncId, Linkage, Module}; use internment::ArcIntern; - -use crate::backend::error::BackendError; -use crate::backend::Backend; +use std::collections::{hash_map, HashMap}; /// When we're talking about variables, it's handy to just have a table that points /// from a variable to "what to do if you want to reference this variable", which is /// agnostic about whether the variable is local, global, an argument, etc. Since /// the type of that function is a little bit annoying, we summarize it here. -enum ReferenceBuilder { +pub enum ReferenceBuilder { Global(ConstantType, GlobalValue), Local(ConstantType, cranelift_frontend::Variable), Argument(ConstantType, entities::Value), @@ -29,7 +28,8 @@ impl ReferenceBuilder { match self { ReferenceBuilder::Global(ty, gv) => { let cranelift_type = ir::Type::from(*ty); - let value = builder.ins().symbol_value(cranelift_type, *gv); + let ptr_value = builder.ins().symbol_value(types::I64, *gv); + let value = builder.ins().load(cranelift_type, MemFlags::new(), ptr_value, 0); (value, *ty) } @@ -81,11 +81,44 @@ impl Backend { program: Program, ) -> Result { let mut generated_body = vec![]; + let mut variables = HashMap::new(); + + for (top_level_name, top_level_type) in program.get_top_level_variables() { + match top_level_type { + Type::Function(argument_types, return_type) => { + let func_id = self.declare_function( + top_level_name.as_str(), + Linkage::Export, + argument_types, + *return_type, + )?; + self.defined_functions.insert(top_level_name, func_id); + } + + Type::Primitive(pt) => { + let data_id = self.module.declare_data( + top_level_name.as_str(), + Linkage::Export, + true, + false, + )?; + self.module.define_data(data_id, &pt.blank_data())?; + self.defined_symbols + .insert(top_level_name, (data_id, pt.into())); + } + } + } + + let void = Type::Primitive(PrimitiveType::Void); + let main_func_id = + self.declare_function(function_name, Linkage::Export, vec![], void.clone())?; + self.defined_functions + .insert(ArcIntern::new(function_name.to_string()), main_func_id); for item in program.items { match item { TopLevel::Function(name, args, rettype, body) => { - self.compile_function(name.as_str(), &args, rettype, body)?; + self.compile_function(&mut variables, name.as_str(), &args, rettype, body)?; } TopLevel::Statement(stmt) => { @@ -94,8 +127,8 @@ impl Backend { } } - let void = Type::Primitive(PrimitiveType::Void); self.compile_function( + &mut variables, function_name, &[], void.clone(), @@ -103,14 +136,47 @@ impl Backend { ) } + fn declare_function( + &mut self, + name: &str, + linkage: Linkage, + argument_types: Vec, + return_type: Type, + ) -> Result { + let basic_signature = Signature { + params: argument_types + .iter() + .map(|t| self.translate_type(t)) + .collect(), + returns: if return_type == Type::Primitive(PrimitiveType::Void) { + vec![] + } else { + vec![self.translate_type(&return_type)] + }, + call_conv: CallConv::triple_default(&self.platform), + }; + + // this generates the handle for the function that we'll eventually want to + // return to the user. For now, we declare all functions defined by this + // function as public/global/exported, although we may want to reconsider + // this decision later. + self.module + .declare_function(name, linkage, &basic_signature) + } + /// Compile the given function. pub fn compile_function( &mut self, + variables: &mut HashMap, function_name: &str, arguments: &[(Variable, Type)], return_type: Type, body: Expression, ) -> Result { + // reset the next variable counter. this value shouldn't matter; hopefully + // we won't be using close to 2^32 variables! + self.reset_local_variable_tracker(); + let basic_signature = Signature { params: arguments .iter() @@ -124,17 +190,23 @@ impl Backend { call_conv: CallConv::triple_default(&self.platform), }; - // reset the next variable counter. this value shouldn't matter; hopefully - // we won't be using close to 2^32 variables! - self.reset_local_variable_tracker(); - // this generates the handle for the function that we'll eventually want to // return to the user. For now, we declare all functions defined by this // function as public/global/exported, although we may want to reconsider // this decision later. - let func_id = - self.module - .declare_function(function_name, Linkage::Export, &basic_signature)?; + let interned_name = ArcIntern::new(function_name.to_string()); + let func_id = match self.defined_functions.entry(interned_name) { + hash_map::Entry::Occupied(entry) => *entry.get(), + hash_map::Entry::Vacant(vac) => { + let func_id = self.module.declare_function( + function_name, + Linkage::Export, + &basic_signature, + )?; + vac.insert(func_id); + func_id + } + }; // Next we have to generate the compilation context for the rest of this // function. Currently, we generate a fresh context for every function. @@ -145,14 +217,6 @@ impl Backend { let user_func_name = UserFuncName::user(0, func_id.as_u32()); ctx.func = Function::with_name_signature(user_func_name, basic_signature); - // Let's start creating the variable table we'll use when we're dereferencing - // them later. This table is a little interesting because instead of pointing - // from data to data, we're going to point from data (the variable) to an - // action to take if we encounter that variable at some later point. This - // makes it nice and easy to have many different ways to access data, such - // as globals, function arguments, etc. - let mut variables: ScopedMap, ReferenceBuilder> = ScopedMap::new(); - // At the outer-most scope of things, we'll put global variables we've defined // elsewhere in the program. for (name, (data_id, ty)) in self.defined_symbols.iter() { @@ -160,12 +224,6 @@ impl Backend { variables.insert(name.clone(), ReferenceBuilder::Global(*ty, local_data)); } - // Once we have these, we're going to actually push a level of scope and - // add our arguments. We push scope because if there happen to be any with - // the same name (their shouldn't be, but just in case), we want the arguments - // to win. - variables.new_scope(); - // Finally (!), we generate the function builder that we're going to use to // make this function! let mut fctx = FunctionBuilderContext::new(); @@ -192,7 +250,7 @@ impl Backend { builder.switch_to_block(main_block); - let (value, _) = self.compile_expression(body, &mut variables, &mut builder)?; + let (value, _) = self.compile_expression(body, variables, &mut builder)?; // Now that we're done, inject a return function (one with no actual value; basically // the equivalent of Rust's `return;`). We then seal the block (which lets Cranelift @@ -221,7 +279,7 @@ impl Backend { fn compile_expression( &mut self, expr: Expression, - variables: &mut ScopedMap, + variables: &mut HashMap, builder: &mut FunctionBuilder, ) -> Result<(entities::Value, ConstantType), BackendError> { match expr { @@ -282,6 +340,29 @@ impl Backend { (ConstantType::U64, Type::Primitive(PrimitiveType::U64)) => Ok((val, val_type)), + (ConstantType::Void, Type::Primitive(PrimitiveType::Void)) => { + Ok((val, val_type)) + } + + (ConstantType::U8, Type::Primitive(PrimitiveType::I16)) => { + Ok((builder.ins().uextend(types::I16, val), ConstantType::I16)) + } + (ConstantType::U8, Type::Primitive(PrimitiveType::I32)) => { + Ok((builder.ins().uextend(types::I32, val), ConstantType::I32)) + } + (ConstantType::U8, Type::Primitive(PrimitiveType::I64)) => { + Ok((builder.ins().uextend(types::I64, val), ConstantType::I64)) + } + (ConstantType::U16, Type::Primitive(PrimitiveType::I32)) => { + Ok((builder.ins().uextend(types::I32, val), ConstantType::I32)) + } + (ConstantType::U16, Type::Primitive(PrimitiveType::I64)) => { + Ok((builder.ins().uextend(types::I64, val), ConstantType::I64)) + } + (ConstantType::U32, Type::Primitive(PrimitiveType::I64)) => { + Ok((builder.ins().uextend(types::I64, val), ConstantType::I64)) + } + _ => Err(BackendError::InvalidTypeCast { from: val_type.into(), to: target_type, @@ -327,7 +408,7 @@ impl Backend { } Expression::Block(_, _, mut exprs) => match exprs.pop() { - None => Ok((builder.ins().iconst(types::I8, 0), ConstantType::I8)), + None => Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)), Some(last) => { for inner in exprs { // we can ignore all of these return values and such, because we @@ -354,7 +435,7 @@ impl Backend { // Look up the value for the variable. Because this might be a // global variable (and that requires special logic), we just turn // this into an `Expression` and re-use the logic in that implementation. - let fake_ref = ValueOrRef::Ref(ann, Type::Primitive(PrimitiveType::U8), var); + let fake_ref = ValueOrRef::Ref(ann, Type::Primitive(PrimitiveType::U8), var.clone()); let (val, vtype) = self.compile_value_or_ref(fake_ref, variables, builder)?; let vtype_repr = builder.ins().iconst(types::I64, vtype as i64); @@ -379,7 +460,7 @@ impl Backend { print_func_ref, &[buffer_ptr, name_ptr, vtype_repr, casted_val], ); - Ok((builder.ins().iconst(types::I8, 0), ConstantType::I8)) + Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)) } Expression::Bind(_, name, _, expr) => { @@ -390,7 +471,7 @@ impl Backend { builder.declare_var(variable, ir_type); builder.def_var(variable, value); variables.insert(name, ReferenceBuilder::Local(value_type, variable)); - Ok((builder.ins().iconst(types::I8, 0), ConstantType::I8)) + Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)) } } } @@ -400,7 +481,7 @@ impl Backend { fn compile_value_or_ref( &self, valref: ValueOrRef, - variables: &ScopedMap, + variables: &HashMap, builder: &mut FunctionBuilder, ) -> Result<(entities::Value, ConstantType), BackendError> { match valref { @@ -453,3 +534,23 @@ impl Backend { } } } + +impl PrimitiveType { + fn blank_data(&self) -> DataDescription { + let (size, alignment) = match self { + PrimitiveType::Void => (8, 8), + PrimitiveType::U8 => (1, 1), + PrimitiveType::U16 => (2, 2), + PrimitiveType::U32 => (4, 4), + PrimitiveType::U64 => (4, 4), + PrimitiveType::I8 => (1, 1), + PrimitiveType::I16 => (2, 2), + PrimitiveType::I32 => (4, 4), + PrimitiveType::I64 => (4, 4), + }; + let mut result = DataDescription::new(); + result.define_zeroinit(size); + result.set_align(alignment); + result + } +} diff --git a/src/backend/runtime.rs b/src/backend/runtime.rs index 1df45a3..6362176 100644 --- a/src/backend/runtime.rs +++ b/src/backend/runtime.rs @@ -119,7 +119,7 @@ extern "C" fn runtime_print( Ok(ConstantType::U16) => format!("{} = {}u16", reconstituted, value as u16), Ok(ConstantType::U32) => format!("{} = {}u32", reconstituted, value as u32), Ok(ConstantType::U64) => format!("{} = {}u64", reconstituted, value as u64), - Err(_) => format!("{} = {}", reconstituted, value), + Err(_) => format!("{} = {}", reconstituted, value, vtype_repr), }; if let Some(output_buffer) = unsafe { output_buffer.as_mut() } { diff --git a/src/eval/primtype.rs b/src/eval/primtype.rs index fed57f3..e86ae69 100644 --- a/src/eval/primtype.rs +++ b/src/eval/primtype.rs @@ -191,6 +191,13 @@ impl PrimitiveType { (PrimitiveType::I64, Value::I32(x)) => Ok(Value::I64(*x as i64)), (PrimitiveType::I64, Value::I64(x)) => Ok(Value::I64(*x)), + (PrimitiveType::I16, Value::U8(x)) => Ok(Value::I16(*x as i16)), + (PrimitiveType::I32, Value::U8(x)) => Ok(Value::I32(*x as i32)), + (PrimitiveType::I64, Value::U8(x)) => Ok(Value::I64(*x as i64)), + (PrimitiveType::I32, Value::U16(x)) => Ok(Value::I32(*x as i32)), + (PrimitiveType::I64, Value::U16(x)) => Ok(Value::I64(*x as i64)), + (PrimitiveType::I64, Value::U32(x)) => Ok(Value::I64(*x as i64)), + (PrimitiveType::Void, Value::Void) => Ok(Value::Void), _ => Err(PrimOpError::UnsafeCast { diff --git a/src/ir.rs b/src/ir.rs index bb4d0bd..e058e66 100644 --- a/src/ir.rs +++ b/src/ir.rs @@ -16,5 +16,6 @@ mod arbitrary; pub mod ast; mod eval; mod strings; +mod top_level; pub use ast::*; diff --git a/src/ir/arbitrary.rs b/src/ir/arbitrary.rs index 47aed0b..acf3cc8 100644 --- a/src/ir/arbitrary.rs +++ b/src/ir/arbitrary.rs @@ -1,5 +1,5 @@ use crate::eval::PrimitiveType; -use crate::ir::{Expression, Primitive, Program, TopLevel, Type, Value, ValueOrRef, Variable}; +use crate::ir::{Expression, Primitive, Program, TopLevel, Type, TypeWithVoid, Value, ValueOrRef, Variable}; use crate::syntax::Location; use crate::util::scoped_map::ScopedMap; use proptest::strategy::{NewTree, Strategy, ValueTree}; @@ -288,14 +288,25 @@ fn generate_random_expression( ExpressionType::Block => { let num_stmts = BLOCK_LENGTH_DISTRIBUTION.sample(rng); let mut stmts = Vec::new(); - let mut last_type = Type::Primitive(PrimitiveType::Void); + + if num_stmts == 0 { + return Expression::Block(Location::manufactured(), Type::void(), stmts); + } env.new_scope(); - for _ in 0..num_stmts { - let next = generate_random_expression(rng, env); - last_type = next.type_of(); + for _ in 1..num_stmts { + let mut next = generate_random_expression(rng, env); + let next_type = next.type_of(); + if !next_type.is_void() { + let name = generate_random_name(rng); + env.insert(name.clone(), next_type.clone()); + next = Expression::Bind(Location::manufactured(), name, next_type, Box::new(next)); + } stmts.push(next); } + let last_expr = generate_random_expression(rng, env); + let last_type = last_expr.type_of(); + stmts.push(last_expr); env.release_scope(); Expression::Block(Location::manufactured(), last_type, stmts) diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 78c6998..c065414 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -6,7 +6,10 @@ use crate::{ use internment::ArcIntern; use pretty::{BoxAllocator, DocAllocator, Pretty}; use proptest::arbitrary::Arbitrary; -use std::{fmt, str::FromStr, sync::atomic::AtomicUsize}; +use std::convert::TryFrom; +use std::fmt; +use std::str::FromStr; +use std::sync::atomic::AtomicUsize; use super::arbitrary::ProgramGenerator; @@ -97,6 +100,19 @@ pub enum TopLevel { Function(Variable, Vec<(Variable, Type)>, Type, Expression), } +impl TopLevel { + /// Return the type of the item, as inferred or recently + /// computed. + pub fn type_of(&self) -> T { + match self { + TopLevel::Statement(expr) => expr.type_of(), + TopLevel::Function(_, args, ret, _) => { + T::build_function_type(args.iter().map(|(_, t)| t.clone()).collect(), ret.clone()) + } + } + } +} + impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b TopLevel where A: 'a, @@ -148,7 +164,7 @@ pub enum Expression { } impl Expression { - /// Return a reference to the type of the expression, as inferred or recently + /// Return the type of the expression, as inferred or recently /// computed. pub fn type_of(&self) -> Type { match self { @@ -242,6 +258,16 @@ where } } +impl Expression { + pub fn to_pretty(&self) -> String { + let arena = pretty::Arena::<()>::new(); + let doc = self.pretty(&arena); + let mut output_bytes = Vec::new(); + doc.render(72, &mut output_bytes).unwrap(); + String::from_utf8(output_bytes).expect("pretty generates valid utf-8") + } +} + /// A type representing the primitives allowed in the language. /// /// Having this as an enumeration avoids a lot of "this should not happen" @@ -565,27 +591,56 @@ impl TypeOrVar { } } +impl PartialEq for TypeOrVar { + fn eq(&self, other: &Type) -> bool { + match other { + Type::Function(a, b) => match self { + TypeOrVar::Function(x, y) => x == a && y.as_ref() == b.as_ref(), + _ => false, + }, + + Type::Primitive(a) => match self { + TypeOrVar::Primitive(x) => a == x, + _ => false, + }, + } + } +} + pub trait TypeWithVoid { fn void() -> Self; + fn is_void(&self) -> bool; } impl TypeWithVoid for Type { fn void() -> Self { Type::Primitive(PrimitiveType::Void) } + + fn is_void(&self) -> bool { + self == &Type::Primitive(PrimitiveType::Void) + } } impl TypeWithVoid for TypeOrVar { fn void() -> Self { TypeOrVar::Primitive(PrimitiveType::Void) } + + fn is_void(&self) -> bool { + self == &TypeOrVar::Primitive(PrimitiveType::Void) + } } -//impl From for TypeOrVar { -// fn from(value: Type) -> Self { -// TypeOrVar::Type(value) -// } -//} +pub trait TypeWithFunction: Sized { + fn build_function_type(arg_types: Vec, ret_type: Self) -> Self; +} + +impl TypeWithFunction for Type { + fn build_function_type(arg_types: Vec, ret_type: Self) -> Self { + Type::Function(arg_types, Box::new(ret_type)) + } +} impl> From for TypeOrVar { fn from(value: T) -> Self { @@ -598,3 +653,24 @@ impl> From for TypeOrVar { } } } + +impl TryFrom for Type { + type Error = TypeOrVar; + + fn try_from(value: TypeOrVar) -> Result { + match value { + TypeOrVar::Function(args, ret) => { + let args = args + .into_iter() + .map(Type::try_from) + .collect::>()?; + let ret = Type::try_from(*ret)?; + + Ok(Type::Function(args, Box::new(ret))) + } + + TypeOrVar::Primitive(t) => Ok(Type::Primitive(t)), + _ => Err(value), + } + } +} diff --git a/src/ir/top_level.rs b/src/ir/top_level.rs new file mode 100644 index 0000000..c3459fb --- /dev/null +++ b/src/ir/top_level.rs @@ -0,0 +1,45 @@ +use crate::ir::{Expression, Program, TopLevel, TypeWithFunction, TypeWithVoid, Variable}; +use std::collections::HashMap; + +impl Program { + /// Retrieve the complete set of variables that are defined at the top level of + /// this program. + pub fn get_top_level_variables(&self) -> HashMap { + let mut result = HashMap::new(); + + for item in self.items.iter() { + result.extend(item.get_top_level_variables()); + } + + result + } +} + +impl TopLevel { + /// Retrieve the complete set of variables that are defined at the top level of + /// this top-level item. + /// + /// For functions, this is the function name. For expressions this can be a little + /// bit more complicated, as it sort of depends on the block structuring. + pub fn get_top_level_variables(&self) -> HashMap { + match self { + TopLevel::Function(name, _, _, _) => HashMap::from([(name.clone(), self.type_of())]), + TopLevel::Statement(expr) => expr.get_top_level_variables(), + } + } +} + +impl Expression { + /// Retrieve the complete set of variables that are defined at the top level of + /// this expression. Basically, returns the variable named in bind. + pub fn get_top_level_variables(&self) -> HashMap { + match self { + Expression::Bind(_, name, ty, expr) => { + let mut tlvs = expr.get_top_level_variables(); + tlvs.insert(name.clone(), ty.clone()); + tlvs + }, + _ => HashMap::new(), + } + } +} diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 2ec0894..de005ec 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -154,6 +154,19 @@ impl PartialEq for Expression { } } +impl Expression { + /// Get the location of the expression in the source file (if there is one). + pub fn location(&self) -> &Location { + match self { + Expression::Value(loc, _) => loc, + Expression::Reference(loc, _) => loc, + Expression::Cast(loc, _, _) => loc, + Expression::Primitive(loc, _, _) => loc, + Expression::Block(loc, _) => loc, + } + } +} + /// A value from the source syntax #[derive(Clone, Debug, PartialEq, Eq)] pub enum Value { diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs index 5a2bb99..48c1c52 100644 --- a/src/syntax/tokens.rs +++ b/src/syntax/tokens.rs @@ -270,6 +270,7 @@ impl TryFrom for ConstantType { 21 => Ok(ConstantType::I16), 22 => Ok(ConstantType::I32), 23 => Ok(ConstantType::I64), + 255 => Ok(ConstantType::Void), _ => Err(InvalidConstantType::Value(value)), } } diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 4a1800b..7e06013 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -4,6 +4,7 @@ use crate::syntax::{self, ConstantType}; use crate::type_infer::solve::Constraint; use crate::util::scoped_map::ScopedMap; use internment::ArcIntern; +use std::collections::HashMap; use std::str::FromStr; /// This function takes a syntactic program and converts it into the IR version of the @@ -19,7 +20,7 @@ pub fn convert_program( let mut constraint_db = Vec::new(); let mut items = Vec::new(); let mut renames = ScopedMap::new(); - let mut bindings = ScopedMap::new(); + let mut bindings = HashMap::new(); for item in program.items.drain(..) { items.push(convert_top_level( @@ -40,43 +41,68 @@ pub fn convert_top_level( top_level: syntax::TopLevel, constraint_db: &mut Vec, renames: &mut ScopedMap, ArcIntern>, - bindings: &mut ScopedMap, ir::TypeOrVar>, + bindings: &mut HashMap, ir::TypeOrVar>, ) -> ir::TopLevel { match top_level { syntax::TopLevel::Function(name, args, expr) => { - // First, let us figure out what we're going to name this function. If the user + // First, at some point we're going to want to know a location for this function, + // which should either be the name if we have one, or the body if we don't. + let function_location = match name { + None => expr.location().clone(), + Some(ref name) => name.location.clone(), + }; + // Next, let us figure out what we're going to name this function. If the user // didn't provide one, we'll just call it "function:" for them. (We'll // want a name for this function, eventually, so we might as well do it now.) // // If they did provide a name, see if we're shadowed. IF we are, then we'll have // to specialize the name a bit. Otherwise we'll stick with their name. - let funname = match name { + let function_name = match name { None => ir::gensym("function"), Some(unbound) => finalize_name(bindings, renames, unbound), }; - // Now we manufacture types for the inputs and outputs, and then a type for the - // function itself. We're not going to make any claims on these types, yet; they're - // all just unknown type variables we need to work out. - let argtypes: Vec = args.iter().map(|_| ir::TypeOrVar::new()).collect(); + // This function is going to have a type. We don't know what it is, but it'll have + // one. + let function_type = ir::TypeOrVar::new(); + bindings.insert(function_name.clone(), function_type.clone()); + + // Then, let's figure out what to do with the argument names, which similarly + // may need to be renamed. We'll also generate some new type variables to associate + // with all of them. + // + // Note that we want to do all this in a new renaming scope, so that we shadow + // appropriately. + renames.new_scope(); + let arginfo = args + .iter() + .map(|name| { + let new_type = ir::TypeOrVar::new(); + constraint_db.push(Constraint::IsSomething( + name.location.clone(), + new_type.clone(), + )); + let new_name = finalize_name(bindings, renames, name.clone()); + bindings.insert(new_name.clone(), new_type.clone()); + (new_name, new_type) + }) + .collect::>(); + + // Now we manufacture types for the outputs and then a type for the function itself. + // We're not going to make any claims on these types, yet; they're all just unknown + // type variables we need to work out. let rettype = ir::TypeOrVar::new(); - let funtype = ir::TypeOrVar::Function(argtypes.clone(), Box::new(rettype.clone())); - - // Now let's bind these types into the environment. First, we bind our function - // namae to the function type we just generated. - bindings.insert(funname.clone(), funtype); - // And then we attach the argument names to the argument types. (We have to go - // convert all the names, first.) - let iargs: Vec> = - args.iter().map(|x| ArcIntern::new(x.to_string())).collect(); - assert_eq!(argtypes.len(), iargs.len()); - let mut function_args = vec![]; - for ((arg_name, arg_type), orig_name) in iargs.iter().zip(argtypes).zip(args) { - bindings.insert(arg_name.clone(), arg_type.clone()); - function_args.push((arg_name.clone(), arg_type.clone())); - constraint_db.push(Constraint::IsSomething(orig_name.location, arg_type)); - } + let actual_function_type = ir::TypeOrVar::Function( + arginfo.iter().map(|x| x.1.clone()).collect(), + Box::new(rettype.clone()), + ); + constraint_db.push(Constraint::Equivalent( + function_location, + function_type, + actual_function_type, + )); + // Now let's convert the body over to the new IR. let (expr, ty) = convert_expression(expr, constraint_db, renames, bindings); constraint_db.push(Constraint::Equivalent( expr.location().clone(), @@ -84,7 +110,10 @@ pub fn convert_top_level( ty, )); - ir::TopLevel::Function(funname, function_args, rettype, expr) + // Remember to exit this scoping level! + renames.release_scope(); + + ir::TopLevel::Function(function_name, arginfo, rettype, expr) } syntax::TopLevel::Statement(stmt) => { @@ -108,7 +137,7 @@ fn convert_statement( statement: syntax::Statement, constraint_db: &mut Vec, renames: &mut ScopedMap, ArcIntern>, - bindings: &mut ScopedMap, ir::TypeOrVar>, + bindings: &mut HashMap, ir::TypeOrVar>, ) -> ir::Expression { match statement { syntax::Statement::Print(loc, name) => { @@ -152,7 +181,7 @@ fn convert_expression( expression: syntax::Expression, constraint_db: &mut Vec, renames: &mut ScopedMap, ArcIntern>, - bindings: &mut ScopedMap, ir::TypeOrVar>, + bindings: &mut HashMap, ir::TypeOrVar>, ) -> (ir::Expression, ir::TypeOrVar) { match expression { // converting values is mostly tedious, because there's so many cases @@ -339,7 +368,7 @@ fn finalize_expression( } fn finalize_name( - bindings: &ScopedMap, ir::TypeOrVar>, + bindings: &HashMap, ir::TypeOrVar>, renames: &mut ScopedMap, ArcIntern>, name: syntax::Name, ) -> ArcIntern { diff --git a/src/type_infer/finalize.rs b/src/type_infer/finalize.rs index e2af988..1b9e703 100644 --- a/src/type_infer/finalize.rs +++ b/src/type_infer/finalize.rs @@ -105,7 +105,7 @@ fn finalize_type(ty: TypeOrVar, resolutions: &TypeResolutions) -> Type { TypeOrVar::Primitive(x) => Type::Primitive(x), TypeOrVar::Variable(_, tvar) => match resolutions.get(&tvar) { None => panic!("Did not resolve type for type variable {}", tvar), - Some(pt) => Type::Primitive(*pt), + Some(pt) => pt.clone(), }, TypeOrVar::Function(mut args, ret) => Type::Function( args.drain(..) diff --git a/src/type_infer/solve.rs b/src/type_infer/solve.rs index 34127de..0b1b7f8 100644 --- a/src/type_infer/solve.rs +++ b/src/type_infer/solve.rs @@ -1,5 +1,5 @@ use crate::eval::PrimitiveType; -use crate::ir::{Primitive, TypeOrVar}; +use crate::ir::{Primitive, Type, TypeOrVar}; use crate::syntax::Location; use codespan_reporting::diagnostic::Diagnostic; use internment::ArcIntern; @@ -50,7 +50,7 @@ impl fmt::Display for Constraint { } } -pub type TypeResolutions = HashMap, PrimitiveType>; +pub type TypeResolutions = HashMap, Type>; /// The results of type inference; like [`Result`], but with a bit more information. /// @@ -257,18 +257,21 @@ pub fn solve_constraints( ) -> TypeInferenceResult { let mut errors = vec![]; let mut warnings = vec![]; - let mut resolutions = HashMap::new(); + let mut resolutions: HashMap, Type> = HashMap::new(); let mut changed_something = true; - println!("CONSTRAINTS:"); - for constraint in constraint_db.iter() { - println!("{}", constraint); - } - // We want to run this inference endlessly, until either we have solved all of our // constraints. Internal to the loop, we have a check that will make sure that we // do (eventually) stop. while changed_something && !constraint_db.is_empty() { + println!("CONSTRAINT:"); + for constraint in constraint_db.iter() { + println!(" {}", constraint); + } + println!("RESOLUTIONS:"); + for (name, ty) in resolutions.iter() { + println!(" {} = {}", name, ty); + } // Set this to false at the top of the loop. We'll set this to true if we make // progress in any way further down, but having this here prevents us from going // into an infinite look when we can't figure stuff out. @@ -292,9 +295,13 @@ pub fn solve_constraints( Constraint::IsSomething(_, TypeOrVar::Function(_, _)) | Constraint::IsSomething(_, TypeOrVar::Primitive(_)) => changed_something = true, - // Otherwise, we'll keep looking for it. - Constraint::IsSomething(_, TypeOrVar::Variable(_, _)) => { - constraint_db.push(constraint); + // Otherwise, see if we've resolved this variable to anything. If not, add it + // back. + Constraint::IsSomething(_, TypeOrVar::Variable(_, ref name)) => { + if resolutions.get(name).is_none() { + constraint_db.push(constraint); + } + changed_something = true; } // Case #1a: We have two primitive types. If they're equal, we've discharged this @@ -311,35 +318,7 @@ pub fn solve_constraints( changed_something = true; } - // Case #2: One of the two constraints is a primitive, and the other is a variable. - // In this case, we'll check to see if we've resolved the variable, and check for - // equivalence if we have. If we haven't, we'll set that variable to be primitive - // type. - Constraint::Equivalent( - loc, - TypeOrVar::Primitive(t), - TypeOrVar::Variable(_, name), - ) - | Constraint::Equivalent( - loc, - TypeOrVar::Variable(_, name), - TypeOrVar::Primitive(t), - ) => { - match resolutions.get(&name) { - None => { - resolutions.insert(name, t); - } - Some(t2) if &t == t2 => {} - Some(t2) => errors.push(TypeInferenceError::NotEquivalent( - loc, - TypeOrVar::Primitive(t), - TypeOrVar::Primitive(*t2), - )), - } - changed_something = true; - } - - // Case #3: They're both variables. In which case, we'll have to do much the same + // Case #2: They're both variables. In which case, we'll have to do much the same // check, but now on their resolutions. Constraint::Equivalent( ref loc, @@ -350,11 +329,11 @@ pub fn solve_constraints( constraint_db.push(constraint); } (Some(pt), None) => { - resolutions.insert(name2.clone(), *pt); + resolutions.insert(name2.clone(), pt.clone()); changed_something = true; } (None, Some(pt)) => { - resolutions.insert(name1.clone(), *pt); + resolutions.insert(name1.clone(), pt.clone()); changed_something = true; } (Some(pt1), Some(pt2)) if pt1 == pt2 => { @@ -363,13 +342,43 @@ pub fn solve_constraints( (Some(pt1), Some(pt2)) => { errors.push(TypeInferenceError::NotEquivalent( loc.clone(), - TypeOrVar::Primitive(*pt1), - TypeOrVar::Primitive(*pt2), + pt1.clone().into(), + pt2.clone().into(), )); changed_something = true; } }, + // Case #3: One of the two constraints is a primitive, and the other is a variable. + // In this case, we'll check to see if we've resolved the variable, and check for + // equivalence if we have. If we haven't, we'll set that variable to be primitive + // type. + Constraint::Equivalent(loc, t, TypeOrVar::Variable(vloc, name)) + | Constraint::Equivalent(loc, TypeOrVar::Variable(vloc, name), t) => { + match resolutions.get(&name) { + None => match t.try_into() { + Ok(real_type) => { + resolutions.insert(name, real_type); + } + Err(variable_type) => { + constraint_db.push(Constraint::Equivalent( + loc, + variable_type, + TypeOrVar::Variable(vloc, name), + )); + continue; + } + }, + Some(t2) if &t == t2 => {} + Some(t2) => errors.push(TypeInferenceError::NotEquivalent( + loc, + t, + t2.clone().into(), + )), + } + changed_something = true; + } + // Case #4: Like primitives, but for function types. This is a little complicated, because // we first want to resolve all the type variables in the two types, and then see if they're // equivalent. Fortunately, though, we can cheat a bit. What we're going to do is first see @@ -445,7 +454,7 @@ pub fn solve_constraints( Some(nt) => { constraint_db.push(Constraint::FitsInNumType( loc, - TypeOrVar::Primitive(*nt), + nt.clone().into(), val, )); changed_something = true; @@ -474,7 +483,7 @@ pub fn solve_constraints( Some(nt) => { constraint_db.push(Constraint::CanCastTo( loc, - TypeOrVar::Primitive(*nt), + nt.clone().into(), to_type, )); changed_something = true; @@ -494,7 +503,7 @@ pub fn solve_constraints( constraint_db.push(Constraint::CanCastTo( loc, from_type, - TypeOrVar::Primitive(*nt), + nt.clone().into(), )); changed_something = true; } @@ -560,8 +569,7 @@ pub fn solve_constraints( None => constraint_db .push(Constraint::NumericType(loc, TypeOrVar::Variable(vloc, var))), Some(nt) => { - constraint_db - .push(Constraint::NumericType(loc, TypeOrVar::Primitive(*nt))); + constraint_db.push(Constraint::NumericType(loc, nt.clone().into())); changed_something = true; } } @@ -592,10 +600,8 @@ pub fn solve_constraints( TypeOrVar::Variable(vloc, var), )), Some(nt) => { - constraint_db.push(Constraint::ConstantNumericType( - loc, - TypeOrVar::Primitive(*nt), - )); + constraint_db + .push(Constraint::ConstantNumericType(loc, nt.clone().into())); changed_something = true; } } -- 2.53.0 From 7def93878146167517588f225fbddaa7017fe7a6 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Fri, 2 Feb 2024 10:36:28 -0800 Subject: [PATCH 15/59] tests all pass --- src/backend/eval.rs | 40 ++-------------------------------------- 1 file changed, 2 insertions(+), 38 deletions(-) diff --git a/src/backend/eval.rs b/src/backend/eval.rs index 3c639b1..f1c3810 100644 --- a/src/backend/eval.rs +++ b/src/backend/eval.rs @@ -1,12 +1,10 @@ use crate::backend::Backend; use crate::eval::EvalError; -use crate::ir::{Expression, Program, TopLevel, Type}; -use crate::syntax::Location; +use crate::ir::{Expression, Program, Type}; use cranelift_jit::JITModule; use cranelift_object::ObjectModule; #[cfg(test)] use proptest::arbitrary::Arbitrary; -use std::collections::HashMap; use std::path::Path; use target_lexicon::Triple; @@ -56,45 +54,11 @@ impl Backend { //let allocator = Arena::<()>::new(); //program.pretty(&allocator).render(80, &mut std::io::stdout())?; let mut backend = Self::object_file(Triple::host())?; - let mut function_map = HashMap::new(); - let mut main_function_body = vec![]; - - for item in program.items { - match item { - TopLevel::Function(name, args, rettype, body) => { - let function_id = backend.compile_function( - &mut HashMap::new(), - name.as_str(), - args.as_slice(), - rettype, - body, - )?; - function_map.insert(name, function_id); - } - - TopLevel::Statement(stmt) => { - main_function_body.push(stmt); - } - } - } - - let main_function_body = Expression::Block( - Location::manufactured(), - Type::Primitive(crate::eval::PrimitiveType::Void), - main_function_body, - ); - let my_directory = tempfile::tempdir()?; let object_path = my_directory.path().join("object.o"); let executable_path = my_directory.path().join("test_executable"); - backend.compile_function( - &mut HashMap::new(), - "gogogo", - &[], - Type::Primitive(crate::eval::PrimitiveType::Void), - main_function_body, - )?; + backend.compile_program("gogogo", program)?; let bytes = backend.bytes()?; std::fs::write(&object_path, bytes)?; Self::link(&object_path, &executable_path)?; -- 2.53.0 From 9d41cf0da7fffb06e5bc55c42533211e727ef3ec Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Mon, 5 Feb 2024 17:30:16 -0600 Subject: [PATCH 16/59] ran into another type inference problem --- examples/basic/function0001.ngr | 5 +++ src/backend/error.rs | 7 ++-- src/backend/into_crane.rs | 43 ++++++++++++++++++++-- src/eval.rs | 20 +++++++++++ src/ir/arbitrary.rs | 7 ++-- src/ir/ast.rs | 26 +++++++++++--- src/ir/eval.rs | 27 ++++++++++++++ src/ir/top_level.rs | 2 +- src/syntax.rs | 2 +- src/syntax/ast.rs | 6 ++++ src/syntax/eval.rs | 27 ++++++++++++++ src/syntax/parser.lalrpop | 15 ++++++++ src/syntax/pretty.rs | 7 +++- src/syntax/validate.rs | 11 ++++++ src/type_infer/convert.rs | 63 +++++++++++++++++++++++++++++++-- src/type_infer/finalize.rs | 9 +++++ src/type_infer/solve.rs | 14 +++++--- 17 files changed, 267 insertions(+), 24 deletions(-) create mode 100644 examples/basic/function0001.ngr diff --git a/examples/basic/function0001.ngr b/examples/basic/function0001.ngr new file mode 100644 index 0000000..1a845d8 --- /dev/null +++ b/examples/basic/function0001.ngr @@ -0,0 +1,5 @@ +x = 1; +function add_x(y) x + y +a = 3; +result = add_x(a); +print result; \ No newline at end of file diff --git a/src/backend/error.rs b/src/backend/error.rs index e5e2a1e..c6dc995 100644 --- a/src/backend/error.rs +++ b/src/backend/error.rs @@ -53,10 +53,9 @@ pub enum BackendError { impl From for Diagnostic { fn from(value: BackendError) -> Self { match value { - BackendError::Cranelift(me) => { - Diagnostic::error().with_message(format!("Internal cranelift error: {}", me)) - .with_notes(vec![format!("{:?}", me)]) - } + BackendError::Cranelift(me) => Diagnostic::error() + .with_message(format!("Internal cranelift error: {}", me)) + .with_notes(vec![format!("{:?}", me)]), BackendError::BuiltinError(me) => { Diagnostic::error().with_message(format!("Internal runtime function error: {}", me)) } diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index 81a9142..740d101 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -4,7 +4,8 @@ use crate::eval::PrimitiveType; use crate::ir::{Expression, Primitive, Program, TopLevel, Type, Value, ValueOrRef, Variable}; use crate::syntax::{ConstantType, Location}; use cranelift_codegen::ir::{ - self, entities, types, AbiParam, Function, GlobalValue, InstBuilder, MemFlags, Signature, UserFuncName + self, entities, types, AbiParam, Function, GlobalValue, InstBuilder, MemFlags, Signature, + UserFuncName, }; use cranelift_codegen::isa::CallConv; use cranelift_codegen::Context; @@ -29,7 +30,9 @@ impl ReferenceBuilder { ReferenceBuilder::Global(ty, gv) => { let cranelift_type = ir::Type::from(*ty); let ptr_value = builder.ins().symbol_value(types::I64, *gv); - let value = builder.ins().load(cranelift_type, MemFlags::new(), ptr_value, 0); + let value = builder + .ins() + .load(cranelift_type, MemFlags::new(), ptr_value, 0); (value, *ty) } @@ -435,7 +438,8 @@ impl Backend { // Look up the value for the variable. Because this might be a // global variable (and that requires special logic), we just turn // this into an `Expression` and re-use the logic in that implementation. - let fake_ref = ValueOrRef::Ref(ann, Type::Primitive(PrimitiveType::U8), var.clone()); + let fake_ref = + ValueOrRef::Ref(ann, Type::Primitive(PrimitiveType::U8), var.clone()); let (val, vtype) = self.compile_value_or_ref(fake_ref, variables, builder)?; let vtype_repr = builder.ins().iconst(types::I64, vtype as i64); @@ -473,6 +477,39 @@ impl Backend { variables.insert(name, ReferenceBuilder::Local(value_type, variable)); Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)) } + + Expression::Call(_, _, function, args) => { + let (arguments, _argument_types): (Vec<_>, Vec<_>) = args + .into_iter() + .map(|x| self.compile_value_or_ref(x, variables, builder)) + .collect::,BackendError>>()? + .into_iter() + .unzip(); + + match *function { + ValueOrRef::Value(_, _, _) => { + panic!("Can't use a value for a function") + } + + ValueOrRef::Ref(_, result_type, name) => match self.defined_functions.get(&name) { + None => panic!("Couldn't find function {} to call", name), + Some(function) => { + let func_ref = self.module.declare_func_in_func(*function, builder.func); + let call = builder.ins().call(func_ref, &arguments); + let results = builder.inst_results(call); + + match results { + [] => Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)), + [result] => match result_type { + Type::Primitive(ct) => Ok((*result, ct.into())), + Type::Function(_, _) => panic!("return value is a function?"), + } + _ => panic!("don't support multi-value returns yet"), + } + } + } + } + } } } diff --git a/src/eval.rs b/src/eval.rs index f0fee04..182f640 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -39,6 +39,7 @@ mod primtype; mod value; use cranelift_module::ModuleError; +use internment::ArcIntern; pub use primop::PrimOpError; pub use primtype::PrimitiveType; pub use value::Value; @@ -76,6 +77,15 @@ pub enum EvalError { UnknownPrimType(#[from] UnknownPrimType), #[error("Variable lookup failed for {1} at {0:?}")] LookupFailed(crate::syntax::Location, String), + #[error("Attempted to call something that wasn't a function at {0:?} (it was a {1})")] + NotAFunction(crate::syntax::Location, Value), + #[error("Wrong argument call for function ({1:?}) at {0:?}; expected {2}, saw {3}")] + WrongArgCount( + crate::syntax::Location, + Option>, + usize, + usize, + ), } impl PartialEq> for EvalError { @@ -129,6 +139,16 @@ impl PartialEq> for EvalError { EvalError::UnknownPrimType(b) => a == b, _ => false, }, + + EvalError::NotAFunction(a, b) => match other { + EvalError::NotAFunction(x, y) => a == x && b == y, + _ => false, + }, + + EvalError::WrongArgCount(a, b, c, d) => match other { + EvalError::WrongArgCount(w, x, y, z) => a == w && b == x && c == y && d == z, + _ => false, + }, } } } diff --git a/src/ir/arbitrary.rs b/src/ir/arbitrary.rs index acf3cc8..1ac45bb 100644 --- a/src/ir/arbitrary.rs +++ b/src/ir/arbitrary.rs @@ -1,5 +1,7 @@ use crate::eval::PrimitiveType; -use crate::ir::{Expression, Primitive, Program, TopLevel, Type, TypeWithVoid, Value, ValueOrRef, Variable}; +use crate::ir::{ + Expression, Primitive, Program, TopLevel, Type, TypeWithVoid, Value, ValueOrRef, Variable, +}; use crate::syntax::Location; use crate::util::scoped_map::ScopedMap; use proptest::strategy::{NewTree, Strategy, ValueTree}; @@ -300,7 +302,8 @@ fn generate_random_expression( if !next_type.is_void() { let name = generate_random_name(rng); env.insert(name.clone(), next_type.clone()); - next = Expression::Bind(Location::manufactured(), name, next_type, Box::new(next)); + next = + Expression::Bind(Location::manufactured(), name, next_type, Box::new(next)); } stmts.push(next); } diff --git a/src/ir/ast.rs b/src/ir/ast.rs index c065414..cfdc1f4 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -160,6 +160,7 @@ pub enum Expression { Primitive(Location, Type, Primitive, Vec>), Block(Location, Type, Vec>), Print(Location, Variable), + Call(Location, Type, Box>, Vec>), Bind(Location, Variable, Type, Box>), } @@ -173,6 +174,7 @@ impl Expression { Expression::Primitive(_, t, _, _) => t.clone(), Expression::Block(_, t, _) => t.clone(), Expression::Print(_, _) => Type::void(), + Expression::Call(_, t, _, _) => t.clone(), Expression::Bind(_, _, _, _) => Type::void(), } } @@ -186,6 +188,7 @@ impl Expression { Expression::Primitive(l, _, _, _) => l, Expression::Block(l, _, _) => l, Expression::Print(l, _) => l, + Expression::Call(l, _, _, _) => l, Expression::Bind(l, _, _, _) => l, } } @@ -221,6 +224,12 @@ where Expression::Primitive(_, _, op, exprs) => { allocator.text(format!("!!{:?} with {} arguments!!", op, exprs.len())) } + Expression::Call(_, _, fun, args) => { + let args = args.iter().map(|x| x.pretty(allocator)); + let comma_sepped_args = + allocator.intersperse(args, crate::syntax::pretty::CommaSep {}); + fun.pretty(allocator).append(comma_sepped_args.parens()) + } Expression::Block(_, _, exprs) => match exprs.split_last() { None => allocator.text("()"), Some((last, &[])) => last.pretty(allocator), @@ -660,13 +669,20 @@ impl TryFrom for Type { fn try_from(value: TypeOrVar) -> Result { match value { TypeOrVar::Function(args, ret) => { - let args = args - .into_iter() + let converted_args = args + .iter() + .cloned() .map(Type::try_from) - .collect::>()?; - let ret = Type::try_from(*ret)?; + .collect::>(); + let converted_ret = Type::try_from((*ret).clone()); + + if let Ok(args) = converted_args { + if let Ok(ret) = converted_ret { + return Ok(Type::Function(args, Box::new(ret))); + } + } - Ok(Type::Function(args, Box::new(ret))) + Err(TypeOrVar::Function(args, ret)) } TypeOrVar::Primitive(t) => Ok(Type::Primitive(t)), diff --git a/src/ir/eval.rs b/src/ir/eval.rs index 30bf920..27f1e84 100644 --- a/src/ir/eval.rs +++ b/src/ir/eval.rs @@ -103,6 +103,33 @@ where env.insert(name.clone(), value); Ok(Value::Void) } + + Expression::Call(loc, _, fun, args) => { + let function = fun.eval(env)?; + + match function { + Value::Closure(name, mut env, arguments, body) => { + if args.len() != arguments.len() { + return Err(EvalError::WrongArgCount( + loc.clone(), + name, + arguments.len(), + args.len(), + )); + } + + env.new_scope(); + for (name, value) in arguments.into_iter().zip(args.into_iter()) { + let value = value.eval(&mut env)?; + env.insert(name, value); + } + let result = body.eval(&mut env, stdout)?; + env.release_scope(); + Ok(result) + } + _ => Err(EvalError::NotAFunction(loc.clone(), function)), + } + } } } } diff --git a/src/ir/top_level.rs b/src/ir/top_level.rs index c3459fb..728472d 100644 --- a/src/ir/top_level.rs +++ b/src/ir/top_level.rs @@ -38,7 +38,7 @@ impl Expression { let mut tlvs = expr.get_top_level_variables(); tlvs.insert(name.clone(), ty.clone()); tlvs - }, + } _ => HashMap::new(), } } diff --git a/src/syntax.rs b/src/syntax.rs index 0047573..b532f02 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -37,7 +37,7 @@ lalrpop_mod!( parser, "/syntax/parser.rs" ); -mod pretty; +pub mod pretty; mod validate; #[cfg(test)] diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index de005ec..89928e8 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -124,6 +124,7 @@ pub enum Expression { Reference(Location, String), Cast(Location, String, Box), Primitive(Location, String, Vec), + Call(Location, Box, Vec), Block(Location, Vec), } @@ -146,6 +147,10 @@ impl PartialEq for Expression { Expression::Primitive(_, prim2, args2) => prim1 == prim2 && args1 == args2, _ => false, }, + Expression::Call(_, f1, a1) => match other { + Expression::Call(_, f2, a2) => f1 == f2 && a1 == a2, + _ => false, + }, Expression::Block(_, stmts1) => match other { Expression::Block(_, stmts2) => stmts1 == stmts2, _ => false, @@ -162,6 +167,7 @@ impl Expression { Expression::Reference(loc, _) => loc, Expression::Cast(loc, _, _) => loc, Expression::Primitive(loc, _, _) => loc, + Expression::Call(loc, _, _) => loc, Expression::Block(loc, _) => loc, } } diff --git a/src/syntax/eval.rs b/src/syntax/eval.rs index 6093633..e84a0b9 100644 --- a/src/syntax/eval.rs +++ b/src/syntax/eval.rs @@ -118,6 +118,33 @@ impl Expression { Ok(Value::calculate(op, arg_values)?) } + Expression::Call(loc, fun, args) => { + let function = fun.eval(stdout, env)?; + + match function { + Value::Closure(name, mut closure_env, arguments, body) => { + if args.len() != arguments.len() { + return Err(EvalError::WrongArgCount( + loc.clone(), + name, + arguments.len(), + args.len(), + )); + } + + closure_env.new_scope(); + for (name, value) in arguments.into_iter().zip(args.iter()) { + let value = value.eval(stdout, env)?; + closure_env.insert(name, value); + } + let result = body.eval(stdout, &mut closure_env)?; + closure_env.release_scope(); + Ok(result) + } + _ => Err(EvalError::NotAFunction(loc.clone(), function)), + } + } + Expression::Block(_, stmts) => { let mut result = Value::Void; diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index 8c66e63..03ac419 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -205,9 +205,24 @@ UnaryExpression: Expression = { Expression::Primitive(Location::new(file_idx, l..le), "-".to_string(), vec![e]), "<" "> ">" => Expression::Cast(Location::new(file_idx, l..le), v.to_string(), Box::new(e)), + CallExpression, +} + +CallExpression: Expression = { + "(" ")" => + Expression::Call(Location::new(file_idx, s..e), Box::new(f), args), AtomicExpression, } +CallArguments: Vec = { + => vec![], + => vec![e], + "," => { + args.push(e); + args + } +} + // finally, we describe our lowest-level expressions as "atomic", because // they cannot be further divided into parts AtomicExpression: Expression = { diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index 859f51f..6cafc43 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -105,6 +105,11 @@ where let comma_sepped_args = allocator.intersperse(args, CommaSep {}); call.append(comma_sepped_args.parens()) } + Expression::Call(_, fun, args) => { + let args = args.iter().map(|x| x.pretty(allocator)); + let comma_sepped_args = allocator.intersperse(args, CommaSep {}); + fun.pretty(allocator).append(comma_sepped_args.parens()) + } Expression::Block(_, stmts) => match stmts.split_last() { None => allocator.text("()"), Some((last, &[])) => last.pretty(allocator), @@ -167,7 +172,7 @@ fn type_suffix(x: &Option) -> &'static str { } #[derive(Clone, Copy)] -struct CommaSep {} +pub struct CommaSep {} impl<'a, D, A> Pretty<'a, D, A> for CommaSep where diff --git a/src/syntax/validate.rs b/src/syntax/validate.rs index 1503c65..331d5e6 100644 --- a/src/syntax/validate.rs +++ b/src/syntax/validate.rs @@ -212,6 +212,17 @@ impl Expression { (errors, warnings) } + Expression::Call(_, func, args) => { + let (mut errors, mut warnings) = func.validate(variable_map); + + for arg in args.iter() { + let (mut e, mut w) = arg.validate(variable_map); + errors.append(&mut e); + warnings.append(&mut w); + } + + (errors, warnings) + } Expression::Block(_, stmts) => { let mut errors = vec![]; let mut warnings = vec![]; diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 7e06013..9f2f98a 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -286,9 +286,7 @@ fn convert_expression( let (aexp, atype) = convert_expression(arg, constraint_db, renames, bindings); let (aprereqs, asimple) = simplify_expr(aexp); - if let Some(prereq) = aprereqs { - prereqs.push(prereq); - } + merge_prereq(&mut prereqs, aprereqs); nargs.push(asimple); atypes.push(atype); } @@ -313,6 +311,59 @@ fn convert_expression( } } + syntax::Expression::Call(loc, fun, args) => { + let return_type = ir::TypeOrVar::new(); + let arg_types = args + .iter() + .map(|_| ir::TypeOrVar::new()) + .collect::>(); + + let (new_fun, new_fun_type) = + convert_expression(*fun, constraint_db, renames, bindings); + let target_fun_type = + ir::TypeOrVar::Function(arg_types.clone(), Box::new(return_type.clone())); + constraint_db.push(Constraint::Equivalent( + loc.clone(), + new_fun_type, + target_fun_type, + )); + let mut prereqs = vec![]; + + let (fun_prereqs, fun) = simplify_expr(new_fun); + merge_prereq(&mut prereqs, fun_prereqs); + + let new_args = args + .into_iter() + .zip(arg_types.into_iter()) + .map(|(arg, target_type)| { + let (new_arg, inferred_type) = + convert_expression(arg, constraint_db, renames, bindings); + let location = new_arg.location().clone(); + let (arg_prereq, new_valref) = simplify_expr(new_arg); + merge_prereq(&mut prereqs, arg_prereq); + constraint_db.push(Constraint::Equivalent( + location, + inferred_type, + target_type, + )); + new_valref + }) + .collect(); + + let last_call = + ir::Expression::Call(loc.clone(), return_type.clone(), Box::new(fun), new_args); + + if prereqs.is_empty() { + (last_call, return_type) + } else { + prereqs.push(last_call); + ( + ir::Expression::Block(loc, return_type.clone(), prereqs), + return_type, + ) + } + } + syntax::Expression::Block(loc, stmts) => { let mut ret_type = ir::TypeOrVar::Primitive(PrimitiveType::Void); let mut exprs = vec![]; @@ -381,6 +432,12 @@ fn finalize_name( } } +fn merge_prereq(left: &mut Vec, prereq: Option) { + if let Some(item) = prereq { + left.push(item) + } +} + #[cfg(test)] mod tests { // use super::*; diff --git a/src/type_infer/finalize.rs b/src/type_infer/finalize.rs index 1b9e703..4173794 100644 --- a/src/type_infer/finalize.rs +++ b/src/type_infer/finalize.rs @@ -91,6 +91,15 @@ fn finalize_expression( Expression::Print(loc, var) => Expression::Print(loc, var), + Expression::Call(loc, ty, fun, args) => Expression::Call( + loc, + finalize_type(ty, resolutions), + Box::new(finalize_val_or_ref(*fun, resolutions)), + args.into_iter() + .map(|x| finalize_val_or_ref(x, resolutions)) + .collect(), + ), + Expression::Bind(loc, var, ty, subexp) => Expression::Bind( loc, var, diff --git a/src/type_infer/solve.rs b/src/type_infer/solve.rs index 0b1b7f8..88ea802 100644 --- a/src/type_infer/solve.rs +++ b/src/type_infer/solve.rs @@ -192,7 +192,7 @@ impl From for Diagnostic { } TypeInferenceError::CouldNotSolve(Constraint::Equivalent(loc, a, b)) => { loc.labelled_error("internal error").with_message(format!( - "could not determine if {} and {:#?} were equivalent", + "could not determine if {} and {} were equivalent", a, b )) } @@ -264,7 +264,7 @@ pub fn solve_constraints( // constraints. Internal to the loop, we have a check that will make sure that we // do (eventually) stop. while changed_something && !constraint_db.is_empty() { - println!("CONSTRAINT:"); + println!("\n\n\nCONSTRAINT:"); for constraint in constraint_db.iter() { println!(" {}", constraint); } @@ -300,8 +300,9 @@ pub fn solve_constraints( Constraint::IsSomething(_, TypeOrVar::Variable(_, ref name)) => { if resolutions.get(name).is_none() { constraint_db.push(constraint); + } else { + changed_something = true; } - changed_something = true; } // Case #1a: We have two primitive types. If they're equal, we've discharged this @@ -355,12 +356,15 @@ pub fn solve_constraints( // type. Constraint::Equivalent(loc, t, TypeOrVar::Variable(vloc, name)) | Constraint::Equivalent(loc, TypeOrVar::Variable(vloc, name), t) => { + println!("IN THIS CASE with {}", name); match resolutions.get(&name) { None => match t.try_into() { Ok(real_type) => { + println!(" HERE with {} and {}", name, real_type); resolutions.insert(name, real_type); } Err(variable_type) => { + println!(" REJECTED INTO RETURN with {} and {}", name, variable_type); constraint_db.push(Constraint::Equivalent( loc, variable_type, @@ -369,7 +373,9 @@ pub fn solve_constraints( continue; } }, - Some(t2) if &t == t2 => {} + Some(t2) if &t == t2 => { + println!(" MATCHED at {} == {}", t, t2); + } Some(t2) => errors.push(TypeInferenceError::NotEquivalent( loc, t, -- 2.53.0 From 7edaf747aa47148e2f19d0028e78d86ca27ffa5f Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 17 Feb 2024 09:38:12 -0800 Subject: [PATCH 17/59] it runs! gets the wrong answer, but runs! --- build.rs | 2 +- src/backend/into_crane.rs | 37 ++-- src/bin/ngrun.rs | 12 ++ src/ir/ast.rs | 34 +++- src/ir/eval.rs | 14 +- src/syntax/validate.rs | 4 + src/type_infer/convert.rs | 2 +- src/type_infer/solve.rs | 412 +++++++++++++++++++++++++++++++++++++- 8 files changed, 491 insertions(+), 26 deletions(-) diff --git a/build.rs b/build.rs index f883c2b..50fe205 100644 --- a/build.rs +++ b/build.rs @@ -70,7 +70,7 @@ fn generate_tests(f: &mut File, path_so_far: PathBuf) -> std::io::Result<()> { writeln!(f, " let (errors, _) = syntax.validate();")?; writeln!( f, - " assert_eq!(errors.len(), 0, \"file should have no validation errors\");" + " assert_eq!(errors.len(), 0, \"file should have no validation errors, but saw: {{:?}}\", errors);" )?; writeln!(f, " let syntax_result = syntax.eval();")?; writeln!( diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index 740d101..ce1acad 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -482,7 +482,7 @@ impl Backend { let (arguments, _argument_types): (Vec<_>, Vec<_>) = args .into_iter() .map(|x| self.compile_value_or_ref(x, variables, builder)) - .collect::,BackendError>>()? + .collect::, BackendError>>()? .into_iter() .unzip(); @@ -491,20 +491,29 @@ impl Backend { panic!("Can't use a value for a function") } - ValueOrRef::Ref(_, result_type, name) => match self.defined_functions.get(&name) { - None => panic!("Couldn't find function {} to call", name), - Some(function) => { - let func_ref = self.module.declare_func_in_func(*function, builder.func); - let call = builder.ins().call(func_ref, &arguments); - let results = builder.inst_results(call); - - match results { - [] => Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)), - [result] => match result_type { - Type::Primitive(ct) => Ok((*result, ct.into())), - Type::Function(_, _) => panic!("return value is a function?"), + ValueOrRef::Ref(_, result_type, name) => { + match self.defined_functions.get(&name) { + None => panic!("Couldn't find function {} to call", name), + Some(function) => { + let func_ref = + self.module.declare_func_in_func(*function, builder.func); + let call = builder.ins().call(func_ref, &arguments); + let results = builder.inst_results(call); + + match results { + [] => Ok(( + builder.ins().iconst(types::I64, 0), + ConstantType::Void, + )), + [result] => match result_type { + Type::Primitive(ct) => Ok((*result, ct.into())), + Type::Function(_, rt) => match *rt { + Type::Function(_, _) => panic!("function returns a function?"), + Type::Primitive(ct) => Ok((*result, ct.into())), + } + }, + _ => panic!("don't support multi-value returns yet"), } - _ => panic!("don't support multi-value returns yet"), } } } diff --git a/src/bin/ngrun.rs b/src/bin/ngrun.rs index 1d5ca43..4bce462 100644 --- a/src/bin/ngrun.rs +++ b/src/bin/ngrun.rs @@ -58,6 +58,18 @@ fn main() { } }; + let (errors, warnings) = syntax.validate(); + let stop = !errors.is_empty(); + for error in errors { + emit(error.into()); + } + for warning in warnings { + emit(warning.into()); + } + if stop { + return; + } + if cli.interpreter == Interpreter::Syntax { match syntax.eval() { Err(e) => println!("Evaluation error: {}", e), diff --git a/src/ir/ast.rs b/src/ir/ast.rs index cfdc1f4..cc1d873 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -598,6 +598,38 @@ impl TypeOrVar { pub fn new_located(loc: Location) -> Self { TypeOrVar::Variable(loc, gensym("t")) } + + /// Try replacing the given type variable with the given type, returning true if anything + /// was changed. + pub fn replace(&mut self, name: &ArcIntern, replace_with: &TypeOrVar) -> bool { + match self { + TypeOrVar::Variable(_, var_name) if name == var_name => { + *self = replace_with.clone(); + true + } + + TypeOrVar::Variable(_, _) => false, + + TypeOrVar::Function(args, ret) => { + ret.replace(name, replace_with) + | args.iter_mut().any(|x| x.replace(name, replace_with)) + } + + TypeOrVar::Primitive(_) => false, + } + } + + /// Returns whether or not this type is resolved (meaning that it contains no type + /// variables.) + pub fn is_resolved(&self) -> bool { + match self { + TypeOrVar::Variable(_, _) => false, + TypeOrVar::Primitive(_) => true, + TypeOrVar::Function(args, ret) => { + args.iter().all(TypeOrVar::is_resolved) && ret.is_resolved() + } + } + } } impl PartialEq for TypeOrVar { @@ -675,7 +707,7 @@ impl TryFrom for Type { .map(Type::try_from) .collect::>(); let converted_ret = Type::try_from((*ret).clone()); - + if let Ok(args) = converted_args { if let Ok(ret) = converted_ret { return Ok(Type::Function(args, Box::new(ret))); diff --git a/src/ir/eval.rs b/src/ir/eval.rs index 27f1e84..4d5841f 100644 --- a/src/ir/eval.rs +++ b/src/ir/eval.rs @@ -108,7 +108,7 @@ where let function = fun.eval(env)?; match function { - Value::Closure(name, mut env, arguments, body) => { + Value::Closure(name, mut closure_env, arguments, body) => { if args.len() != arguments.len() { return Err(EvalError::WrongArgCount( loc.clone(), @@ -118,13 +118,13 @@ where )); } - env.new_scope(); - for (name, value) in arguments.into_iter().zip(args.into_iter()) { - let value = value.eval(&mut env)?; - env.insert(name, value); + closure_env.new_scope(); + for (name, value) in arguments.into_iter().zip(args) { + let value = value.eval(env)?; + closure_env.insert(name, value); } - let result = body.eval(&mut env, stdout)?; - env.release_scope(); + let result = body.eval(&mut closure_env, stdout)?; + closure_env.release_scope(); Ok(result) } _ => Err(EvalError::NotAFunction(loc.clone(), function)), diff --git a/src/syntax/validate.rs b/src/syntax/validate.rs index 331d5e6..9c89885 100644 --- a/src/syntax/validate.rs +++ b/src/syntax/validate.rs @@ -13,6 +13,7 @@ use std::str::FromStr; /// that we're not going to be able to work through. As with most /// of these errors, we recommend converting this to a [`Diagnostic`] /// and using [`codespan_reporting`] to present them to the user. +#[derive(Debug)] pub enum Error { UnboundVariable(Location, String), UnknownType(Location, String), @@ -83,6 +84,9 @@ impl Program { let mut warnings = vec![]; for stmt in self.items.iter() { + if let TopLevel::Function(Some(name), _, _) = stmt { + bound_variables.insert(name.to_string(), name.location.clone()); + } let (mut new_errors, mut new_warnings) = stmt.validate_with_bindings(bound_variables); errors.append(&mut new_errors); warnings.append(&mut new_warnings); diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 9f2f98a..d93d811 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -334,7 +334,7 @@ fn convert_expression( let new_args = args .into_iter() - .zip(arg_types.into_iter()) + .zip(arg_types) .map(|(arg, target_type)| { let (new_arg, inferred_type) = convert_expression(arg, constraint_db, renames, bindings); diff --git a/src/type_infer/solve.rs b/src/type_infer/solve.rs index 88ea802..fb668d2 100644 --- a/src/type_infer/solve.rs +++ b/src/type_infer/solve.rs @@ -27,6 +27,8 @@ pub enum Constraint { Equivalent(Location, TypeOrVar, TypeOrVar), /// The given type can be resolved to something IsSomething(Location, TypeOrVar), + /// The given type can be negated + IsSigned(Location, TypeOrVar), } impl fmt::Display for Constraint { @@ -46,6 +48,34 @@ impl fmt::Display for Constraint { Constraint::ConstantNumericType(_, ty) => write!(f, "CONST_NUMERIC {}", ty), Constraint::Equivalent(_, ty, ty2) => write!(f, "EQUIVALENT {} => {}", ty, ty2), Constraint::IsSomething(_, ty) => write!(f, "SOMETHING {}", ty), + Constraint::IsSigned(_, ty) => write!(f, "SIGNED {}", ty), + } + } +} + +impl Constraint { + /// Replace all instances of the name (anywhere! including on the left hand side of equivalences!) + /// with the given type. + /// + /// Returns whether or not anything was changed in the constraint. + fn replace(&mut self, name: &ArcIntern, replace_with: &TypeOrVar) -> bool { + match self { + Constraint::Printable(_, ty) => ty.replace(name, replace_with), + Constraint::FitsInNumType(_, ty, _) => ty.replace(name, replace_with), + Constraint::CanCastTo(_, ty1, ty2) => { + ty1.replace(name, replace_with) || ty2.replace(name, replace_with) + } + Constraint::ConstantNumericType(_, ty) => ty.replace(name, replace_with), + Constraint::Equivalent(_, ty1, ty2) => { + ty1.replace(name, replace_with) || ty2.replace(name, replace_with) + } + Constraint::IsSigned(_, ty) => ty.replace(name, replace_with), + Constraint::IsSomething(_, ty) => ty.replace(name, replace_with), + Constraint::NumericType(_, ty) => ty.replace(name, replace_with), + Constraint::ProperPrimitiveArgs(_, _, args, ret) => { + ret.replace(name, replace_with) + | args.iter_mut().any(|x| x.replace(name, replace_with)) + } } } } @@ -112,7 +142,8 @@ pub enum TypeInferenceError { CannotSafelyCast(Location, PrimitiveType, PrimitiveType), /// The primitive invocation provided the wrong number of arguments. WrongPrimitiveArity(Location, Primitive, usize, usize, usize), - /// We cannot cast between function types at the moment. + /// We cannot cast between the given function types, usually because they + /// have different argument lengths CannotCastBetweenFunctinoTypes(Location, TypeOrVar, TypeOrVar), /// We cannot cast from a function type to something else. CannotCastFromFunctionType(Location, TypeOrVar), @@ -122,6 +153,10 @@ pub enum TypeInferenceError { CannotMakeNumberAFunction(Location, TypeOrVar, Option), /// We had a constraint we just couldn't solve. CouldNotSolve(Constraint), + /// Functions are not printable. + FunctionsAreNotPrintable(Location), + /// The given type isn't signed, and can't be negated + IsNotSigned(Location, PrimitiveType), } impl From for Diagnostic { @@ -184,6 +219,11 @@ impl From for Diagnostic { "cannot use a constant as a function type".to_string() }) .with_message(format!("function type was {}", t)), + TypeInferenceError::FunctionsAreNotPrintable(loc) => loc + .labelled_error("cannot print function values"), + TypeInferenceError::IsNotSigned(loc, pt) => loc + .labelled_error(format!("type {} is not signed", pt)) + .with_message("and so it cannot be negated"), TypeInferenceError::CouldNotSolve(Constraint::CanCastTo(loc, a, b)) => { loc.labelled_error("internal error").with_message(format!( "could not determine if it was safe to cast from {} to {:#?}", @@ -220,6 +260,9 @@ impl From for Diagnostic { loc.labelled_error("could not infer type") .with_message("Could not find *any* type information; is this an unused function argument?") } + TypeInferenceError::CouldNotSolve(Constraint::IsSigned(loc, t)) => loc + .labelled_error("internal error") + .with_message(format!("could not infer that type {} was signed", t)), } } } @@ -257,6 +300,366 @@ pub fn solve_constraints( ) -> TypeInferenceResult { let mut errors = vec![]; let mut warnings = vec![]; + + loop { + let mut changed_something = false; + let mut all_constraints_solved = true; + let mut new_constraints = vec![]; + + println!("\n\n\nCONSTRAINT:"); + for constraint in constraint_db.iter() { + println!(" {}", constraint); + } + + while let Some(constraint) = constraint_db.pop() { + match constraint { + // The basic philosophy of this match block is that, for each constraint, we're + // going to start seeing if we can just solve (or abandon) the constraint. Then, + // if we can't, we'll just chuck it back on our list for later. + + // Checks on whether we can cast from one thing to another! + Constraint::CanCastTo( + loc, + TypeOrVar::Primitive(from_type), + TypeOrVar::Primitive(to_type), + ) => { + if !from_type.can_cast_to(&to_type) { + errors.push(TypeInferenceError::CannotSafelyCast( + loc, from_type, to_type, + )); + } + changed_something = true; + } + + Constraint::CanCastTo( + loc, + TypeOrVar::Function(args1, ret1), + TypeOrVar::Function(args2, ret2), + ) => { + if args1.len() == args2.len() { + new_constraints.push(Constraint::Equivalent(loc.clone(), *ret1, *ret2)); + for (arg1, arg2) in args1.into_iter().zip(args2) { + new_constraints.push(Constraint::Equivalent(loc.clone(), arg1, arg2)) + } + all_constraints_solved = false; + } else { + errors.push(TypeInferenceError::CannotCastBetweenFunctinoTypes( + loc, + TypeOrVar::Function(args1, ret1), + TypeOrVar::Function(args2, ret2), + )); + } + changed_something = true; + } + + Constraint::CanCastTo( + loc, + TypeOrVar::Function(_, _), + pt @ TypeOrVar::Primitive(_), + ) => { + errors.push(TypeInferenceError::CannotCastFromFunctionType(loc, pt)); + changed_something = true; + } + + Constraint::CanCastTo( + loc, + pt @ TypeOrVar::Primitive(_), + TypeOrVar::Function(_, _), + ) => { + errors.push(TypeInferenceError::CannotCastToFunctionType(loc, pt)); + changed_something = true; + } + + // if we're testing if an actual primitive type is numeric, that's pretty easy + Constraint::ConstantNumericType(_, TypeOrVar::Primitive(_)) => { + changed_something = true; + } + + // if we're testing if a function type is numeric, then throw a useful warning + Constraint::ConstantNumericType(loc, t @ TypeOrVar::Function(_, _)) => { + errors.push(TypeInferenceError::CannotMakeNumberAFunction(loc, t, None)); + changed_something = true; + } + + // if we're testing if a number can fit into a numeric type, we can just do that! + Constraint::FitsInNumType(loc, TypeOrVar::Primitive(ctype), val) => { + match ctype.max_value() { + None => errors.push(TypeInferenceError::NotANumber(loc, ctype)), + + Some(max_value) if max_value < val => { + errors.push(TypeInferenceError::ConstantTooLarge(loc, ctype, val)); + } + + Some(_) => {} + } + changed_something = true; + } + + // if we're testing if a function type can fit into a numeric type, that's a problem + Constraint::FitsInNumType(loc, t @ TypeOrVar::Function(_, _), val) => { + errors.push(TypeInferenceError::CannotMakeNumberAFunction( + loc, + t, + Some(val), + )); + changed_something = true; + } + + // if we want to know if a type is something, and it is something, then we're done + Constraint::IsSomething(_, TypeOrVar::Function(_, _)) + | Constraint::IsSomething(_, TypeOrVar::Primitive(_)) => { + changed_something = true; + } + + // if we want to know if something is signed, we can check its primitive type + Constraint::IsSigned(loc, TypeOrVar::Primitive(pt)) => { + if !pt.valid_operators().contains(&("-", 1)) { + errors.push(TypeInferenceError::IsNotSigned(loc, pt)); + } + changed_something = true; + } + + // again with the functions and the numbers + Constraint::IsSigned(loc, t @ TypeOrVar::Function(_, _)) => { + errors.push(TypeInferenceError::CannotCastFromFunctionType(loc, t)); + changed_something = true; + } + + // if we're testing if an actual primitive type is numeric, that's pretty easy + Constraint::NumericType(_, TypeOrVar::Primitive(_)) => { + changed_something = true; + } + + // if we're testing if a function type is numeric, then throw a useful warning + Constraint::NumericType(loc, t @ TypeOrVar::Function(_, _)) => { + errors.push(TypeInferenceError::CannotMakeNumberAFunction(loc, t, None)); + changed_something = true; + } + + // all of our primitive types are printable + Constraint::Printable(_, TypeOrVar::Primitive(_)) => { + changed_something = true; + } + + // function types are definitely not printable + Constraint::Printable(loc, TypeOrVar::Function(_, _)) => { + errors.push(TypeInferenceError::FunctionsAreNotPrintable(loc)); + changed_something = true; + } + + Constraint::ProperPrimitiveArgs(loc, prim, mut args, ret) => match prim { + Primitive::Plus | Primitive::Minus | Primitive::Times | Primitive::Divide + if args.len() == 2 => + { + let right = args.pop().expect("2>0"); + let left = args.pop().expect("2>1"); + + new_constraints.push(Constraint::NumericType(loc.clone(), left.clone())); + new_constraints.push(Constraint::Equivalent( + loc.clone(), + left.clone(), + right, + )); + new_constraints.push(Constraint::Equivalent(loc.clone(), left, ret)); + changed_something = true; + all_constraints_solved = false; + } + + Primitive::Minus if args.len() == 1 => { + let value = args.pop().expect("1>0"); + new_constraints.push(Constraint::NumericType(loc.clone(), value.clone())); + new_constraints.push(Constraint::IsSigned(loc.clone(), value.clone())); + new_constraints.push(Constraint::Equivalent(loc, value, ret)); + changed_something = true; + all_constraints_solved = false; + } + + Primitive::Plus | Primitive::Times | Primitive::Divide => { + errors.push(TypeInferenceError::WrongPrimitiveArity( + loc, + prim, + 2, + 2, + args.len(), + )); + changed_something = true; + } + + Primitive::Minus => { + errors.push(TypeInferenceError::WrongPrimitiveArity( + loc, + prim, + 1, + 2, + args.len(), + )); + changed_something = true; + } + }, + + // Some equivalences we can/should solve directly + Constraint::Equivalent( + loc, + TypeOrVar::Primitive(pt1), + TypeOrVar::Primitive(pt2), + ) => { + if pt1 != pt2 { + errors.push(TypeInferenceError::NotEquivalent( + loc, + TypeOrVar::Primitive(pt1), + TypeOrVar::Primitive(pt2), + )); + } + changed_something = true; + } + + Constraint::Equivalent( + loc, + pt @ TypeOrVar::Primitive(_), + ft @ TypeOrVar::Function(_, _), + ) + | Constraint::Equivalent( + loc, + ft @ TypeOrVar::Function(_, _), + pt @ TypeOrVar::Primitive(_), + ) => { + errors.push(TypeInferenceError::NotEquivalent(loc, pt, ft)); + changed_something = true; + } + + Constraint::Equivalent( + loc, + TypeOrVar::Function(args1, ret1), + TypeOrVar::Function(args2, ret2), + ) => { + if args1.len() != args2.len() { + let t1 = TypeOrVar::Function(args1, ret1); + let t2 = TypeOrVar::Function(args2, ret2); + errors.push(TypeInferenceError::NotEquivalent(loc, t1, t2)); + } else { + for (left, right) in args1.into_iter().zip(args2) { + new_constraints.push(Constraint::Equivalent(loc.clone(), left, right)); + } + new_constraints.push(Constraint::Equivalent(loc, *ret1, *ret2)); + all_constraints_solved = false; + } + + changed_something = true; + } + + Constraint::Equivalent(_, TypeOrVar::Variable(_, ref name), ref rhs) => { + changed_something |= replace_variable(&mut constraint_db, name, rhs); + changed_something |= replace_variable(&mut new_constraints, name, rhs); + all_constraints_solved &= rhs.is_resolved(); + new_constraints.push(constraint); + } + + Constraint::Equivalent(loc, lhs, rhs @ TypeOrVar::Variable(_, _)) => { + new_constraints.push(Constraint::Equivalent(loc, rhs, lhs)); + changed_something = true; + all_constraints_solved = false; + } + + Constraint::CanCastTo(_, TypeOrVar::Variable(_, _), _) + | Constraint::CanCastTo(_, _, TypeOrVar::Variable(_, _)) + | Constraint::ConstantNumericType(_, TypeOrVar::Variable(_, _)) + | Constraint::FitsInNumType(_, TypeOrVar::Variable(_, _), _) + | Constraint::IsSomething(_, TypeOrVar::Variable(_, _)) + | Constraint::IsSigned(_, TypeOrVar::Variable(_, _)) + | Constraint::NumericType(_, TypeOrVar::Variable(_, _)) + | Constraint::Printable(_, TypeOrVar::Variable(_, _)) => { + all_constraints_solved = false; + new_constraints.push(constraint); + } + } + } + + if all_constraints_solved { + let result = new_constraints + .into_iter() + .map(|constraint| match constraint { + Constraint::Equivalent(_, TypeOrVar::Variable(_, name), result) => { + match result.try_into() { + Err(e) => panic!("Ended up with complex type {}", e), + Ok(v) => (name, v), + } + } + _ => panic!("Had something that wasn't an equivalence left at the end!"), + }) + .collect(); + return TypeInferenceResult::Success { result, warnings }; + } + + if !changed_something { + let mut addendums = vec![]; + + new_constraints.retain(|x| { + if let Constraint::ConstantNumericType(loc, t) = x { + let resty = TypeOrVar::Primitive(PrimitiveType::U64); + addendums.push(Constraint::Equivalent( + loc.clone(), + t.clone(), + resty.clone(), + )); + warnings.push(TypeInferenceWarning::DefaultedTo(loc.clone(), resty)); + false + } else { + true + } + }); + + if addendums.is_empty() { + if errors.is_empty() { + errors = new_constraints + .into_iter() + .map(TypeInferenceError::CouldNotSolve) + .collect(); + } + return TypeInferenceResult::Failure { errors, warnings }; + } + + new_constraints.append(&mut addendums); + } + + constraint_db = new_constraints; + } +} + +/// Replace the given variable with the given type everywhere in the list of contraints. +/// +/// Returns whether anything was changed, at all, anywhere in the database. Note that +/// this will destroy all instances of the name, both on the left and right hand sides, +/// so you better make sure you can re-introduce *something* about this name after this +/// runs if you don't want to lose it. (If you do want to lose it, of course, go ahead.) +fn replace_variable( + constraint_db: &mut Vec, + variable: &ArcIntern, + replace_with: &TypeOrVar, +) -> bool { + let mut changed_anything = false; + + for constraint in constraint_db { + changed_anything |= constraint.replace(variable, replace_with); + } + + changed_anything +} + +/// Solve all the constraints in the provided database. +/// +/// This process can take a bit, so you might not want to do it multiple times. Basically, +/// it's going to grind on these constraints until either it figures them out, or it stops +/// making progress. I haven't done the math on the constraints to even figure out if this +/// is guaranteed to halt, though, let alone terminate in some reasonable amount of time. +/// +/// The return value is a type inference result, which pairs some warnings with either a +/// successful set of type resolutions (mappings from type variables to their values), or +/// a series of inference errors. +pub fn _solve_constraints_old( + mut constraint_db: Vec, +) -> TypeInferenceResult { + let mut errors = vec![]; + let mut warnings = vec![]; let mut resolutions: HashMap, Type> = HashMap::new(); let mut changed_something = true; @@ -364,7 +767,10 @@ pub fn solve_constraints( resolutions.insert(name, real_type); } Err(variable_type) => { - println!(" REJECTED INTO RETURN with {} and {}", name, variable_type); + println!( + " REJECTED INTO RETURN with {} and {}", + name, variable_type + ); constraint_db.push(Constraint::Equivalent( loc, variable_type, @@ -702,6 +1108,8 @@ pub fn solve_constraints( changed_something = true; } }, + + _ => panic!("unexpected constraint"), } } -- 2.53.0 From 26bd7e90fdf6cf2d42ef01f273167a38d0bf6a68 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Thu, 22 Feb 2024 13:47:06 -0800 Subject: [PATCH 18/59] getting closer ... --- examples/basic/function0001.ngr | 2 + runtime/rts.c | 2 +- src/backend/into_crane.rs | 114 +++++++++++++++++++++----------- src/compiler.rs | 19 ++++++ src/ir/arbitrary.rs | 8 ++- src/ir/ast.rs | 35 ++++++---- src/ir/eval.rs | 11 +-- src/type_infer/convert.rs | 4 +- src/type_infer/finalize.rs | 7 +- 9 files changed, 137 insertions(+), 65 deletions(-) diff --git a/examples/basic/function0001.ngr b/examples/basic/function0001.ngr index 1a845d8..6cdb5c3 100644 --- a/examples/basic/function0001.ngr +++ b/examples/basic/function0001.ngr @@ -1,5 +1,7 @@ x = 1; function add_x(y) x + y a = 3; +print x; result = add_x(a); +print x; print result; \ No newline at end of file diff --git a/runtime/rts.c b/runtime/rts.c index d8eba53..dbcd759 100644 --- a/runtime/rts.c +++ b/runtime/rts.c @@ -32,7 +32,7 @@ void print(char *_ignore, char *variable_name, int64_t vtype, int64_t value) { printf("%s = \n", variable_name); break; default: - printf("%s = UNKNOWN VTYPE %d\n", variable_name, vtype); + printf("%s = UNKNOWN VTYPE %lld\n", variable_name, vtype); } } diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index ce1acad..2526984 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -24,28 +24,6 @@ pub enum ReferenceBuilder { Argument(ConstantType, entities::Value), } -impl ReferenceBuilder { - fn refer_to(&self, builder: &mut FunctionBuilder) -> (entities::Value, ConstantType) { - match self { - ReferenceBuilder::Global(ty, gv) => { - let cranelift_type = ir::Type::from(*ty); - let ptr_value = builder.ins().symbol_value(types::I64, *gv); - let value = builder - .ins() - .load(cranelift_type, MemFlags::new(), ptr_value, 0); - (value, *ty) - } - - ReferenceBuilder::Local(ty, var) => { - let value = builder.use_var(*var); - (value, *ty) - } - - ReferenceBuilder::Argument(ty, val) => (*val, *ty), - } - } -} - impl Backend { /// Translate the given IR type into an ABI parameter type for cranelift, as /// best as possible. @@ -105,6 +83,7 @@ impl Backend { true, false, )?; + println!("defining {} with primitive type {}", top_level_name, pt); self.module.define_data(data_id, &pt.blank_data())?; self.defined_symbols .insert(top_level_name, (data_id, pt.into())); @@ -146,6 +125,7 @@ impl Backend { argument_types: Vec, return_type: Type, ) -> Result { + println!("Declaring {:?} function {}", linkage, name); let basic_signature = Signature { params: argument_types .iter() @@ -176,6 +156,22 @@ impl Backend { return_type: Type, body: Expression, ) -> Result { + println!("Compiling function {}", function_name); + { + use pretty::{DocAllocator, Pretty}; + let allocator = pretty::BoxAllocator; + allocator + .text("Function body:") + .append(allocator.hardline()) + .append(body.pretty(&allocator)) + .append(allocator.hardline()) + .1 + .render_colored( + 70, + pretty::termcolor::StandardStream::stdout(pretty::termcolor::ColorChoice::Auto), + ) + .expect("rendering works"); + } // reset the next variable counter. this value shouldn't matter; hopefully // we won't be using close to 2^32 variables! self.reset_local_variable_tracker(); @@ -423,24 +419,23 @@ impl Backend { } }, - Expression::Print(ann, var) => { + Expression::Print(_, var) => { // Get the output buffer (or null) from our general compilation context. let buffer_ptr = self.output_buffer_ptr(); let buffer_ptr = builder.ins().iconst(types::I64, buffer_ptr as i64); // Get a reference to the string we want to print. - let string_data_id = self.string_reference(var.as_ref())?; + let var_name = match var { + ValueOrRef::Ref(_, _, ref name) => name.as_ref(), + ValueOrRef::Value(_, _, _) => "", + }; + let string_data_id = self.string_reference(var_name)?; let local_name_ref = self .module .declare_data_in_func(string_data_id, builder.func); let name_ptr = builder.ins().symbol_value(types::I64, local_name_ref); - // Look up the value for the variable. Because this might be a - // global variable (and that requires special logic), we just turn - // this into an `Expression` and re-use the logic in that implementation. - let fake_ref = - ValueOrRef::Ref(ann, Type::Primitive(PrimitiveType::U8), var.clone()); - let (val, vtype) = self.compile_value_or_ref(fake_ref, variables, builder)?; + let (val, vtype) = self.compile_value_or_ref(var, variables, builder)?; let vtype_repr = builder.ins().iconst(types::I64, vtype as i64); @@ -472,10 +467,30 @@ impl Backend { let ir_type = ir::Type::from(value_type); let variable = self.generate_local(); - builder.declare_var(variable, ir_type); - builder.def_var(variable, value); - variables.insert(name, ReferenceBuilder::Local(value_type, variable)); - Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)) + match variables.get(&name) { + Some(ReferenceBuilder::Global(_, global_value)) => { + let pointer = self.module.target_config().pointer_type(); + let pointer_to = builder.ins().symbol_value(pointer, *global_value); + println!("STORE {}: cranelift_type {} origin type {:?}", name, value, value_type); + builder.ins().store(MemFlags::new(), value, pointer_to, 0); + Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)) + } + + Some(ReferenceBuilder::Argument(_, _)) => { + panic!("Attempt to mutate an argument {}", name) + } + + Some(ReferenceBuilder::Local(_, _)) => { + panic!("Attempt to mutate local {}", name); + } + + None => { + builder.declare_var(variable, ir_type); + builder.def_var(variable, value); + variables.insert(name, ReferenceBuilder::Local(value_type, variable)); + Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)) + } + } } Expression::Call(_, _, function, args) => { @@ -508,9 +523,11 @@ impl Backend { [result] => match result_type { Type::Primitive(ct) => Ok((*result, ct.into())), Type::Function(_, rt) => match *rt { - Type::Function(_, _) => panic!("function returns a function?"), + Type::Function(_, _) => { + panic!("function returns a function?") + } Type::Primitive(ct) => Ok((*result, ct.into())), - } + }, }, _ => panic!("don't support multi-value returns yet"), } @@ -530,6 +547,7 @@ impl Backend { variables: &HashMap, builder: &mut FunctionBuilder, ) -> Result<(entities::Value, ConstantType), BackendError> { + println!("compile_value_or_ref {:?}", valref); match valref { ValueOrRef::Value(_, _, val) => match val { Value::I8(_, v) => { @@ -575,7 +593,25 @@ impl Backend { }, ValueOrRef::Ref(_, _, name) => match variables.get(&name) { None => Err(BackendError::VariableLookupFailure(name)), - Some(x) => Ok(x.refer_to(builder)), + Some(ReferenceBuilder::Global(ty, gv)) => { + let pointer_to = self.module.target_config().pointer_type(); + let pointer_value = builder.ins().symbol_value(pointer_to, *gv); + let cranelift_type = ir::Type::from(*ty); + println!("READ {}: cranelift_type {} origin type {:?}", name, cranelift_type, ty); + let value = + builder + .ins() + .load(cranelift_type, MemFlags::new(), pointer_value, 0); + + Ok((value, *ty)) + } + + Some(ReferenceBuilder::Argument(ctype, val)) => Ok((*val, *ctype)), + + Some(ReferenceBuilder::Local(ctype, var)) => { + let value = builder.use_var(*var); + Ok((value, *ctype)) + } }, } } @@ -588,11 +624,11 @@ impl PrimitiveType { PrimitiveType::U8 => (1, 1), PrimitiveType::U16 => (2, 2), PrimitiveType::U32 => (4, 4), - PrimitiveType::U64 => (4, 4), + PrimitiveType::U64 => (8, 8), PrimitiveType::I8 => (1, 1), PrimitiveType::I16 => (2, 2), PrimitiveType::I32 => (4, 4), - PrimitiveType::I64 => (4, 4), + PrimitiveType::I64 => (8, 8), }; let mut result = DataDescription::new(); result.define_zeroinit(size); diff --git a/src/compiler.rs b/src/compiler.rs index 22c8029..388fccc 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -5,6 +5,7 @@ use codespan_reporting::{ files::SimpleFiles, term::{self, Config}, }; +use cranelift_module::Module; use pretty::termcolor::{ColorChoice, StandardStream}; use target_lexicon::Triple; @@ -134,7 +135,25 @@ impl Compiler { // Finally, send all this to Cranelift for conversion into an object file. let mut backend = Backend::object_file(Triple::host())?; + let unknown = "".to_string(); backend.compile_program("gogogo", ir)?; + println!("FINAL MODULE:"); + println!(" FUNCTIONS:"); + for (_, decl) in backend.module.declarations().get_functions() { + println!( + " {}: {:?}", + decl.name.as_ref().unwrap_or(&unknown), + decl.linkage + ); + } + println!(" DATA:"); + for (_, decl) in backend.module.declarations().get_data_objects() { + println!( + " {}: {:?}", + decl.name.as_ref().unwrap_or(&unknown), + decl.linkage + ); + } Ok(Some(backend.bytes()?)) } diff --git a/src/ir/arbitrary.rs b/src/ir/arbitrary.rs index 1ac45bb..71de979 100644 --- a/src/ir/arbitrary.rs +++ b/src/ir/arbitrary.rs @@ -359,19 +359,21 @@ fn generate_random_expression( .iter() .filter_map(|(variable, ty)| { if ty.is_printable() { - Some(variable.clone()) + Some((variable.clone(), ty.clone())) } else { None } }) - .collect::>(); + .collect::>(); if possible_variables.is_empty() { generate_random_binding(rng, env) } else { + let (variable, var_type) = possible_variables.choose(rng).unwrap(); + Expression::Print( Location::manufactured(), - possible_variables.choose(rng).unwrap().clone(), + ValueOrRef::Ref(Location::manufactured(), var_type.clone(), variable.clone()), ) } } diff --git a/src/ir/ast.rs b/src/ir/ast.rs index cc1d873..7480483 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -61,8 +61,9 @@ pub struct Program { impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b Program where A: 'a, - D: ?Sized + DocAllocator<'a, A>, + D: ?Sized + DocAllocator<'a, A> + 'a, &'b Type: Pretty<'a, D, A>, + pretty::DocBuilder<'a, D, A>: Clone, { fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { let mut result = allocator.nil(); @@ -116,8 +117,9 @@ impl TopLevel { impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b TopLevel where A: 'a, - D: ?Sized + DocAllocator<'a, A>, + D: ?Sized + DocAllocator<'a, A> + 'a, &'b Type: Pretty<'a, D, A>, + pretty::DocBuilder<'a, D, A>: Clone, { fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { match self { @@ -159,7 +161,7 @@ pub enum Expression { Cast(Location, Type, ValueOrRef), Primitive(Location, Type, Primitive, Vec>), Block(Location, Type, Vec>), - Print(Location, Variable), + Print(Location, ValueOrRef), Call(Location, Type, Box>, Vec>), Bind(Location, Variable, Type, Box>), } @@ -197,8 +199,9 @@ impl Expression { impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b Expression where A: 'a, - D: ?Sized + DocAllocator<'a, A>, + D: ?Sized + DocAllocator<'a, A> + 'a, &'b Type: Pretty<'a, D, A>, + pretty::DocBuilder<'a, D, A>: Clone, { fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { match self { @@ -235,24 +238,28 @@ where Some((last, &[])) => last.pretty(allocator), Some((last, start)) => { let mut result = allocator.text("{").append(allocator.hardline()); - - for stmt in start.iter() { - result = result - .append(stmt.pretty(allocator)) + let starts = start.iter().map(|x| { + x.pretty(allocator) .append(allocator.text(";")) - .append(allocator.hardline()); + .append(allocator.hardline()) + .indent(4) + }); + let last = last + .pretty(allocator) + .append(allocator.hardline()) + .indent(4); + + for start in starts { + result = result.append(start); } - result - .append(last.pretty(allocator)) - .append(allocator.hardline()) - .append(allocator.text("}")) + result.append(last).append(allocator.text("}")) } }, Expression::Print(_, var) => allocator .text("print") .append(allocator.space()) - .append(allocator.text(var.as_ref().to_string())), + .append(var.pretty(allocator)), Expression::Bind(_, var, ty, expr) => allocator .text(var.as_ref().to_string()) .append(allocator.space()) diff --git a/src/ir/eval.rs b/src/ir/eval.rs index 4d5841f..5b758ae 100644 --- a/src/ir/eval.rs +++ b/src/ir/eval.rs @@ -89,11 +89,12 @@ where Ok(result) } - Expression::Print(loc, n) => { - let value = env - .get(n) - .cloned() - .ok_or_else(|| EvalError::LookupFailed(loc.clone(), n.to_string()))?; + Expression::Print(_, value) => { + let n = match value { + ValueOrRef::Ref(_, _, ref name) => name.as_str(), + ValueOrRef::Value(_, _, _) => "", + }; + let value = value.eval(env)?; stdout.push_str(&format!("{} = {}\n", n, value)); Ok(Value::Void) } diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index d93d811..3c11de8 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -150,10 +150,11 @@ fn convert_statement( .get(&final_name) .expect("print variable defined before use") .clone(); + println!("varty for {} is {}", final_name, varty); constraint_db.push(Constraint::Printable(loc.clone(), varty.clone())); - ir::Expression::Print(loc, final_name) + ir::Expression::Print(loc.clone(), ir::ValueOrRef::Ref(loc, varty, final_name)) } syntax::Statement::Binding(loc, name, expr) => { @@ -256,6 +257,7 @@ fn convert_expression( .get(&final_name) .cloned() .expect("variable bound before use"); + println!("rtype for {} is {}", final_name, rtype); let refexp = ir::Expression::Atomic(ir::ValueOrRef::Ref(loc, rtype.clone(), final_name)); diff --git a/src/type_infer/finalize.rs b/src/type_infer/finalize.rs index 4173794..12bf00b 100644 --- a/src/type_infer/finalize.rs +++ b/src/type_infer/finalize.rs @@ -89,7 +89,7 @@ fn finalize_expression( Expression::Block(loc, finalize_type(ty, resolutions), final_exprs) } - Expression::Print(loc, var) => Expression::Print(loc, var), + Expression::Print(loc, var) => Expression::Print(loc, finalize_val_or_ref(var, resolutions)), Expression::Call(loc, ty, fun, args) => Expression::Call( loc, @@ -114,7 +114,10 @@ fn finalize_type(ty: TypeOrVar, resolutions: &TypeResolutions) -> Type { TypeOrVar::Primitive(x) => Type::Primitive(x), TypeOrVar::Variable(_, tvar) => match resolutions.get(&tvar) { None => panic!("Did not resolve type for type variable {}", tvar), - Some(pt) => pt.clone(), + Some(pt) => { + println!("Finalizing {} to {}", tvar, pt); + pt.clone() + } }, TypeOrVar::Function(mut args, ret) => Type::Function( args.drain(..) -- 2.53.0 From 0cc2b4ea9d174cef60d0c3d461d5b3422a4f1e9c Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Thu, 22 Feb 2024 14:23:41 -0800 Subject: [PATCH 19/59] wheee --- examples/basic/function0002.ngr | 10 ++++++++++ src/type_infer/solve.rs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 examples/basic/function0002.ngr diff --git a/examples/basic/function0002.ngr b/examples/basic/function0002.ngr new file mode 100644 index 0000000..9d8f014 --- /dev/null +++ b/examples/basic/function0002.ngr @@ -0,0 +1,10 @@ +x = 1; +function add_x(y) x + y +a = 3; +function add_x_twice(y) add_x(y) + x +print x; +result = add_x(a); +print x; +print result; +result = add_x_twice(a); +print result; \ No newline at end of file diff --git a/src/type_infer/solve.rs b/src/type_infer/solve.rs index fb668d2..44e8f90 100644 --- a/src/type_infer/solve.rs +++ b/src/type_infer/solve.rs @@ -328,6 +328,7 @@ pub fn solve_constraints( loc, from_type, to_type, )); } + println!("changed something because finished CanCastTo"); changed_something = true; } @@ -349,6 +350,7 @@ pub fn solve_constraints( TypeOrVar::Function(args2, ret2), )); } + println!("changed something because finished CanCastTo (2)"); changed_something = true; } @@ -358,6 +360,7 @@ pub fn solve_constraints( pt @ TypeOrVar::Primitive(_), ) => { errors.push(TypeInferenceError::CannotCastFromFunctionType(loc, pt)); + println!("changed something because finished CanCastTo (3)"); changed_something = true; } @@ -368,17 +371,20 @@ pub fn solve_constraints( ) => { errors.push(TypeInferenceError::CannotCastToFunctionType(loc, pt)); changed_something = true; + println!("changed something because finished CanCastTo (4)"); } // if we're testing if an actual primitive type is numeric, that's pretty easy Constraint::ConstantNumericType(_, TypeOrVar::Primitive(_)) => { changed_something = true; + println!("changed something because constant numeric type (1)"); } // if we're testing if a function type is numeric, then throw a useful warning Constraint::ConstantNumericType(loc, t @ TypeOrVar::Function(_, _)) => { errors.push(TypeInferenceError::CannotMakeNumberAFunction(loc, t, None)); changed_something = true; + println!("changed something because constant numeric type (2)"); } // if we're testing if a number can fit into a numeric type, we can just do that! @@ -393,6 +399,7 @@ pub fn solve_constraints( Some(_) => {} } changed_something = true; + println!("changed something because fits in numeric type (1)"); } // if we're testing if a function type can fit into a numeric type, that's a problem @@ -403,12 +410,14 @@ pub fn solve_constraints( Some(val), )); changed_something = true; + println!("changed something because fits in numeric type (2)"); } // if we want to know if a type is something, and it is something, then we're done Constraint::IsSomething(_, TypeOrVar::Function(_, _)) | Constraint::IsSomething(_, TypeOrVar::Primitive(_)) => { changed_something = true; + println!("changed something because 1"); } // if we want to know if something is signed, we can check its primitive type @@ -417,34 +426,40 @@ pub fn solve_constraints( errors.push(TypeInferenceError::IsNotSigned(loc, pt)); } changed_something = true; + println!("changed something because 2"); } // again with the functions and the numbers Constraint::IsSigned(loc, t @ TypeOrVar::Function(_, _)) => { errors.push(TypeInferenceError::CannotCastFromFunctionType(loc, t)); changed_something = true; + println!("changed something because 3"); } // if we're testing if an actual primitive type is numeric, that's pretty easy Constraint::NumericType(_, TypeOrVar::Primitive(_)) => { changed_something = true; + println!("changed something because 4"); } // if we're testing if a function type is numeric, then throw a useful warning Constraint::NumericType(loc, t @ TypeOrVar::Function(_, _)) => { errors.push(TypeInferenceError::CannotMakeNumberAFunction(loc, t, None)); changed_something = true; + println!("changed something because 5"); } // all of our primitive types are printable Constraint::Printable(_, TypeOrVar::Primitive(_)) => { changed_something = true; + println!("changed something because 6"); } // function types are definitely not printable Constraint::Printable(loc, TypeOrVar::Function(_, _)) => { errors.push(TypeInferenceError::FunctionsAreNotPrintable(loc)); changed_something = true; + println!("changed something because 7"); } Constraint::ProperPrimitiveArgs(loc, prim, mut args, ret) => match prim { @@ -463,6 +478,7 @@ pub fn solve_constraints( new_constraints.push(Constraint::Equivalent(loc.clone(), left, ret)); changed_something = true; all_constraints_solved = false; + println!("changed something because 8"); } Primitive::Minus if args.len() == 1 => { @@ -472,6 +488,7 @@ pub fn solve_constraints( new_constraints.push(Constraint::Equivalent(loc, value, ret)); changed_something = true; all_constraints_solved = false; + println!("changed something because 9"); } Primitive::Plus | Primitive::Times | Primitive::Divide => { @@ -483,6 +500,7 @@ pub fn solve_constraints( args.len(), )); changed_something = true; + println!("changed something because 10"); } Primitive::Minus => { @@ -494,6 +512,7 @@ pub fn solve_constraints( args.len(), )); changed_something = true; + println!("changed something because 11"); } }, @@ -511,6 +530,7 @@ pub fn solve_constraints( )); } changed_something = true; + println!("changed something because 12"); } Constraint::Equivalent( @@ -524,6 +544,11 @@ pub fn solve_constraints( pt @ TypeOrVar::Primitive(_), ) => { errors.push(TypeInferenceError::NotEquivalent(loc, pt, ft)); + changed_something = true; + println!("changed something because 13"); + } + + Constraint::Equivalent(_, TypeOrVar::Variable(_, name1), TypeOrVar::Variable(_, name2)) if name1 == name2 => { changed_something = true; } @@ -545,12 +570,16 @@ pub fn solve_constraints( } changed_something = true; + println!("changed something because 14"); } Constraint::Equivalent(_, TypeOrVar::Variable(_, ref name), ref rhs) => { changed_something |= replace_variable(&mut constraint_db, name, rhs); changed_something |= replace_variable(&mut new_constraints, name, rhs); all_constraints_solved &= rhs.is_resolved(); + if changed_something { + println!("changed something because 15, maybe (name is {})", name); + } new_constraints.push(constraint); } @@ -558,6 +587,7 @@ pub fn solve_constraints( new_constraints.push(Constraint::Equivalent(loc, rhs, lhs)); changed_something = true; all_constraints_solved = false; + println!("changed something because 16"); } Constraint::CanCastTo(_, TypeOrVar::Variable(_, _), _) @@ -602,6 +632,7 @@ pub fn solve_constraints( resty.clone(), )); warnings.push(TypeInferenceWarning::DefaultedTo(loc.clone(), resty)); + println!("Adding number equivalence"); false } else { true -- 2.53.0 From d663af8a46c52c0fa1b29bc9f1d67e5c5c58e2c8 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Thu, 22 Feb 2024 15:17:56 -0800 Subject: [PATCH 20/59] better --- examples/basic/function0002.ngr | 4 ++-- src/backend/into_crane.rs | 10 ++++++++-- src/type_infer/finalize.rs | 4 +++- src/type_infer/solve.rs | 12 ++++++++---- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/examples/basic/function0002.ngr b/examples/basic/function0002.ngr index 9d8f014..53a0bac 100644 --- a/examples/basic/function0002.ngr +++ b/examples/basic/function0002.ngr @@ -6,5 +6,5 @@ print x; result = add_x(a); print x; print result; -result = add_x_twice(a); -print result; \ No newline at end of file +result2 = add_x_twice(a); +print result2; \ No newline at end of file diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index 2526984..d4053b7 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -471,7 +471,10 @@ impl Backend { Some(ReferenceBuilder::Global(_, global_value)) => { let pointer = self.module.target_config().pointer_type(); let pointer_to = builder.ins().symbol_value(pointer, *global_value); - println!("STORE {}: cranelift_type {} origin type {:?}", name, value, value_type); + println!( + "STORE {}: cranelift_type {} origin type {:?}", + name, value, value_type + ); builder.ins().store(MemFlags::new(), value, pointer_to, 0); Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)) } @@ -597,7 +600,10 @@ impl Backend { let pointer_to = self.module.target_config().pointer_type(); let pointer_value = builder.ins().symbol_value(pointer_to, *gv); let cranelift_type = ir::Type::from(*ty); - println!("READ {}: cranelift_type {} origin type {:?}", name, cranelift_type, ty); + println!( + "READ {}: cranelift_type {} origin type {:?}", + name, cranelift_type, ty + ); let value = builder .ins() diff --git a/src/type_infer/finalize.rs b/src/type_infer/finalize.rs index 12bf00b..59aecf3 100644 --- a/src/type_infer/finalize.rs +++ b/src/type_infer/finalize.rs @@ -89,7 +89,9 @@ fn finalize_expression( Expression::Block(loc, finalize_type(ty, resolutions), final_exprs) } - Expression::Print(loc, var) => Expression::Print(loc, finalize_val_or_ref(var, resolutions)), + Expression::Print(loc, var) => { + Expression::Print(loc, finalize_val_or_ref(var, resolutions)) + } Expression::Call(loc, ty, fun, args) => Expression::Call( loc, diff --git a/src/type_infer/solve.rs b/src/type_infer/solve.rs index 44e8f90..fc9b849 100644 --- a/src/type_infer/solve.rs +++ b/src/type_infer/solve.rs @@ -530,7 +530,7 @@ pub fn solve_constraints( )); } changed_something = true; - println!("changed something because 12"); + println!("changed something because 12"); } Constraint::Equivalent( @@ -545,10 +545,14 @@ pub fn solve_constraints( ) => { errors.push(TypeInferenceError::NotEquivalent(loc, pt, ft)); changed_something = true; - println!("changed something because 13"); + println!("changed something because 13"); } - Constraint::Equivalent(_, TypeOrVar::Variable(_, name1), TypeOrVar::Variable(_, name2)) if name1 == name2 => { + Constraint::Equivalent( + _, + TypeOrVar::Variable(_, name1), + TypeOrVar::Variable(_, name2), + ) if name1 == name2 => { changed_something = true; } @@ -570,7 +574,7 @@ pub fn solve_constraints( } changed_something = true; - println!("changed something because 14"); + println!("changed something because 14"); } Constraint::Equivalent(_, TypeOrVar::Variable(_, ref name), ref rhs) => { -- 2.53.0 From b0aa5bc2222ee351dadaa403a5137bdffa9d313a Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Thu, 22 Feb 2024 17:03:50 -0800 Subject: [PATCH 21/59] Upgrade --- Cargo.toml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4e6b807..94a18b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,27 +9,27 @@ name = "ngr" path = "src/lib.rs" [dependencies] -clap = { version = "4.4.18", features = ["derive"] } +clap = { version = "4.5.1", features = ["derive"] } codespan = "0.11.1" codespan-reporting = "0.11.1" -cranelift-codegen = "0.104.0" -cranelift-jit = "0.104.0" -cranelift-frontend = "0.104.0" -cranelift-module = "0.104.0" -cranelift-native = "0.104.0" -cranelift-object = "0.104.0" +cranelift-codegen = "0.105.1" +cranelift-jit = "0.105.1" +cranelift-frontend = "0.105.1" +cranelift-module = "0.105.1" +cranelift-native = "0.105.1" +cranelift-object = "0.105.1" internment = { version = "0.7.4", default-features = false, features = ["arc"] } lalrpop-util = "0.20.0" lazy_static = "1.4.0" -logos = "0.13.0" +logos = "0.14.0" pretty = { version = "0.12.3", features = ["termcolor"] } proptest = "1.4.0" rand = "0.8.5" rustyline = "13.0.0" -target-lexicon = "0.12.13" -tempfile = "3.9.0" -thiserror = "1.0.56" -anyhow = "1.0.79" +target-lexicon = "0.12.14" +tempfile = "3.10.0" +thiserror = "1.0.57" +anyhow = "1.0.80" [build-dependencies] lalrpop = "0.20.0" -- 2.53.0 From e9fbd275a21b3f3c72ecc36e146822b39a70c113 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 2 Mar 2024 21:19:29 -0800 Subject: [PATCH 22/59] Clean up pretty printing and work on logging. --- Cargo.toml | 2 + src/backend/into_crane.rs | 21 +-- src/bin/ngrc.rs | 1 + src/bin/ngri.rs | 7 +- src/bin/ngrun.rs | 5 +- src/eval/primtype.rs | 31 ++-- src/ir.rs | 1 + src/ir/ast.rs | 316 +------------------------------------ src/ir/pretty.rs | 205 ++++++++++++++++++++++++ src/repl.rs | 6 +- src/syntax/pretty.rs | 70 +++----- src/type_infer/finalize.rs | 18 +-- src/util/pretty.rs | 72 ++++----- 13 files changed, 295 insertions(+), 460 deletions(-) create mode 100644 src/ir/pretty.rs diff --git a/Cargo.toml b/Cargo.toml index 94a18b6..ad4b96b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,8 @@ target-lexicon = "0.12.14" tempfile = "3.10.0" thiserror = "1.0.57" anyhow = "1.0.80" +tracing = "0.1.40" +tracing-subscriber = { version = "0.3.18", features = ["time", "json", "env-filter"] } [build-dependencies] lalrpop = "0.20.0" diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index d4053b7..b260927 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -83,7 +83,7 @@ impl Backend { true, false, )?; - println!("defining {} with primitive type {}", top_level_name, pt); + tracing::info!(name = %top_level_name, data_type = %pt, "defining top-level data"); self.module.define_data(data_id, &pt.blank_data())?; self.defined_symbols .insert(top_level_name, (data_id, pt.into())); @@ -125,7 +125,7 @@ impl Backend { argument_types: Vec, return_type: Type, ) -> Result { - println!("Declaring {:?} function {}", linkage, name); + tracing::info!(linkage = ?linkage, name, "Declaring function"); let basic_signature = Signature { params: argument_types .iter() @@ -148,6 +148,7 @@ impl Backend { } /// Compile the given function. + #[tracing::instrument(level = "debug", skip(self, variables, body))] pub fn compile_function( &mut self, variables: &mut HashMap, @@ -156,22 +157,6 @@ impl Backend { return_type: Type, body: Expression, ) -> Result { - println!("Compiling function {}", function_name); - { - use pretty::{DocAllocator, Pretty}; - let allocator = pretty::BoxAllocator; - allocator - .text("Function body:") - .append(allocator.hardline()) - .append(body.pretty(&allocator)) - .append(allocator.hardline()) - .1 - .render_colored( - 70, - pretty::termcolor::StandardStream::stdout(pretty::termcolor::ColorChoice::Auto), - ) - .expect("rendering works"); - } // reset the next variable counter. this value shouldn't matter; hopefully // we won't be using close to 2^32 variables! self.reset_local_variable_tracker(); diff --git a/src/bin/ngrc.rs b/src/bin/ngrc.rs index e486ec0..a88f4bd 100644 --- a/src/bin/ngrc.rs +++ b/src/bin/ngrc.rs @@ -14,6 +14,7 @@ struct CommandLineArguments { } fn main() { + tracing_subscriber::fmt::init(); let args = CommandLineArguments::parse(); let mut compiler = ngr::Compiler::default(); diff --git a/src/bin/ngri.rs b/src/bin/ngri.rs index 0558de4..482d81c 100644 --- a/src/bin/ngri.rs +++ b/src/bin/ngri.rs @@ -3,6 +3,7 @@ use rustyline::error::ReadlineError; use rustyline::DefaultEditor; fn main() -> Result<(), BackendError> { + tracing_subscriber::fmt::init(); let mut editor = DefaultEditor::new().expect("rustyline works"); let mut line_no = 0; let mut state = ngr::REPL::default(); @@ -19,7 +20,7 @@ fn main() -> Result<(), BackendError> { // it's not clear to me what this could be, but OK Err(ReadlineError::Io(e)) => { - eprintln!("IO error: {}", e); + tracing::error!(error = %e, "IO error"); break; } @@ -31,7 +32,7 @@ fn main() -> Result<(), BackendError> { // what would cause this, but ... #[cfg(not(windows))] Err(ReadlineError::Errno(e)) => { - eprintln!("Unknown syscall error: {}", e); + tracing::error!(error = %e, "Unknown syscall."); break; } @@ -41,7 +42,7 @@ fn main() -> Result<(), BackendError> { // Why on earth are there so many error types? Err(e) => { - eprintln!("Unknown internal error: {}", e); + tracing::error!(error = %e, "Unknown internal error"); break; } } diff --git a/src/bin/ngrun.rs b/src/bin/ngrun.rs index 4bce462..c40cfbb 100644 --- a/src/bin/ngrun.rs +++ b/src/bin/ngrun.rs @@ -42,6 +42,7 @@ fn jit(ir: ngr::ir::Program) -> Result println!("Evaluation error: {}", e), + Err(e) => tracing::error!(error = %e, "Evaluation error"), Ok(v) => print_result(v), } return; @@ -98,7 +99,7 @@ fn main() { if cli.interpreter == Interpreter::IR { match ir.eval() { - Err(e) => println!("Evaluation error: {}", e), + Err(e) => tracing::error!(error = %e, "Evaluation error"), Ok(v) => print_result(v), } return; diff --git a/src/eval/primtype.rs b/src/eval/primtype.rs index e86ae69..f67768e 100644 --- a/src/eval/primtype.rs +++ b/src/eval/primtype.rs @@ -2,6 +2,7 @@ use crate::{ eval::{PrimOpError, Value}, syntax::ConstantType, }; +use pretty::{Arena, DocAllocator, DocBuilder}; use std::{fmt::Display, str::FromStr}; #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] @@ -17,19 +18,27 @@ pub enum PrimitiveType { I64, } +impl PrimitiveType { + pub fn pretty<'a>(&self, allocator: &'a Arena<'a, ()>) -> DocBuilder<'a, Arena<'a, ()>> { + match self { + PrimitiveType::Void => allocator.text("void"), + PrimitiveType::I8 => allocator.text("i8"), + PrimitiveType::I16 => allocator.text("i16"), + PrimitiveType::I32 => allocator.text("i32"), + PrimitiveType::I64 => allocator.text("i64"), + PrimitiveType::U8 => allocator.text("u8"), + PrimitiveType::U16 => allocator.text("u16"), + PrimitiveType::U32 => allocator.text("u32"), + PrimitiveType::U64 => allocator.text("u64"), + } + } +} + impl Display for PrimitiveType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - PrimitiveType::Void => write!(f, "void"), - PrimitiveType::I8 => write!(f, "i8"), - PrimitiveType::I16 => write!(f, "i16"), - PrimitiveType::I32 => write!(f, "i32"), - PrimitiveType::I64 => write!(f, "i64"), - PrimitiveType::U8 => write!(f, "u8"), - PrimitiveType::U16 => write!(f, "u16"), - PrimitiveType::U32 => write!(f, "u32"), - PrimitiveType::U64 => write!(f, "u64"), - } + let arena = Arena::new(); + let doc = self.pretty(&arena); + doc.render_fmt(72, f) } } diff --git a/src/ir.rs b/src/ir.rs index e058e66..48ac76f 100644 --- a/src/ir.rs +++ b/src/ir.rs @@ -15,6 +15,7 @@ mod arbitrary; pub mod ast; mod eval; +mod pretty; mod strings; mod top_level; diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 7480483..ae76a95 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -1,13 +1,8 @@ -use crate::{ - eval::PrimitiveType, - syntax::{self, ConstantType, Location}, - util::pretty::{pretty_comma_separated, PrettySymbol}, -}; +use crate::eval::PrimitiveType; +use crate::syntax::{ConstantType, Location}; use internment::ArcIntern; -use pretty::{BoxAllocator, DocAllocator, Pretty}; use proptest::arbitrary::Arbitrary; use std::convert::TryFrom; -use std::fmt; use std::str::FromStr; use std::sync::atomic::AtomicUsize; @@ -58,29 +53,6 @@ pub struct Program { pub(crate) items: Vec>, } -impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b Program -where - A: 'a, - D: ?Sized + DocAllocator<'a, A> + 'a, - &'b Type: Pretty<'a, D, A>, - pretty::DocBuilder<'a, D, A>: Clone, -{ - fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { - let mut result = allocator.nil(); - - for stmt in self.items.iter() { - // there's probably a better way to do this, rather than constantly - // adding to the end, but this works. - result = result - .append(stmt.pretty(allocator)) - .append(allocator.text(";")) - .append(allocator.hardline()); - } - - result - } -} - impl Arbitrary for Program { type Parameters = (); type Strategy = ProgramGenerator; @@ -114,35 +86,6 @@ impl TopLevel { } } -impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b TopLevel -where - A: 'a, - D: ?Sized + DocAllocator<'a, A> + 'a, - &'b Type: Pretty<'a, D, A>, - pretty::DocBuilder<'a, D, A>: Clone, -{ - fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { - match self { - TopLevel::Function(name, args, _, expr) => allocator - .text("function") - .append(allocator.space()) - .append(allocator.text(name.as_ref().to_string())) - .append(allocator.space()) - .append( - pretty_comma_separated( - allocator, - &args.iter().map(|(x, _)| PrettySymbol::from(x)).collect(), - ) - .parens(), - ) - .append(allocator.space()) - .append(expr.pretty(allocator)), - - TopLevel::Statement(stmt) => stmt.pretty(allocator), - } - } -} - /// The representation of an expression. /// /// Note that expressions, like everything else in this syntax tree, @@ -196,94 +139,6 @@ impl Expression { } } -impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b Expression -where - A: 'a, - D: ?Sized + DocAllocator<'a, A> + 'a, - &'b Type: Pretty<'a, D, A>, - pretty::DocBuilder<'a, D, A>: Clone, -{ - fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { - match self { - Expression::Atomic(x) => x.pretty(allocator), - Expression::Cast(_, t, e) => allocator - .text("<") - .append(t.pretty(allocator)) - .append(allocator.text(">")) - .append(e.pretty(allocator)), - Expression::Primitive(_, _, op, exprs) if exprs.len() == 1 => { - op.pretty(allocator).append(exprs[0].pretty(allocator)) - } - Expression::Primitive(_, _, op, exprs) if exprs.len() == 2 => { - let left = exprs[0].pretty(allocator); - let right = exprs[1].pretty(allocator); - - left.append(allocator.space()) - .append(op.pretty(allocator)) - .append(allocator.space()) - .append(right) - .parens() - } - Expression::Primitive(_, _, op, exprs) => { - allocator.text(format!("!!{:?} with {} arguments!!", op, exprs.len())) - } - Expression::Call(_, _, fun, args) => { - let args = args.iter().map(|x| x.pretty(allocator)); - let comma_sepped_args = - allocator.intersperse(args, crate::syntax::pretty::CommaSep {}); - fun.pretty(allocator).append(comma_sepped_args.parens()) - } - Expression::Block(_, _, exprs) => match exprs.split_last() { - None => allocator.text("()"), - Some((last, &[])) => last.pretty(allocator), - Some((last, start)) => { - let mut result = allocator.text("{").append(allocator.hardline()); - let starts = start.iter().map(|x| { - x.pretty(allocator) - .append(allocator.text(";")) - .append(allocator.hardline()) - .indent(4) - }); - let last = last - .pretty(allocator) - .append(allocator.hardline()) - .indent(4); - - for start in starts { - result = result.append(start); - } - - result.append(last).append(allocator.text("}")) - } - }, - Expression::Print(_, var) => allocator - .text("print") - .append(allocator.space()) - .append(var.pretty(allocator)), - Expression::Bind(_, var, ty, expr) => allocator - .text(var.as_ref().to_string()) - .append(allocator.space()) - .append(allocator.text(":")) - .append(allocator.space()) - .append(ty.pretty(allocator)) - .append(allocator.space()) - .append(allocator.text("=")) - .append(allocator.space()) - .append(expr.pretty(allocator)), - } - } -} - -impl Expression { - pub fn to_pretty(&self) -> String { - let arena = pretty::Arena::<()>::new(); - let doc = self.pretty(&arena); - let mut output_bytes = Vec::new(); - doc.render(72, &mut output_bytes).unwrap(); - String::from_utf8(output_bytes).expect("pretty generates valid utf-8") - } -} - /// A type representing the primitives allowed in the language. /// /// Having this as an enumeration avoids a lot of "this should not happen" @@ -312,27 +167,6 @@ impl FromStr for Primitive { } } -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Primitive -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { - match self { - Primitive::Plus => allocator.text("+"), - Primitive::Minus => allocator.text("-"), - Primitive::Times => allocator.text("*"), - Primitive::Divide => allocator.text("/"), - } - } -} - -impl fmt::Display for Primitive { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - <&Primitive as Pretty<'_, BoxAllocator, ()>>::pretty(self, &BoxAllocator).render_fmt(72, f) - } -} - /// An expression that is always either a value or a reference. /// /// This is the type used to guarantee that we don't nest expressions @@ -344,19 +178,6 @@ pub enum ValueOrRef { Ref(Location, Type, ArcIntern), } -impl<'a, 'b, D, A, Type> Pretty<'a, D, A> for &'b ValueOrRef -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { - match self { - ValueOrRef::Value(_, _, v) => v.pretty(allocator), - ValueOrRef::Ref(_, _, v) => allocator.text(v.as_ref().to_string()), - } - } -} - impl ValueOrRef { pub fn type_of(&self) -> Type { match self { @@ -408,50 +229,6 @@ impl Value { } } -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Value -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { - let pretty_internal = |opt_base: &Option, x, t| { - syntax::Value::Number(*opt_base, Some(t), x).pretty(allocator) - }; - - let pretty_internal_signed = |opt_base, x: i64, t| { - let base = pretty_internal(opt_base, x.unsigned_abs(), t); - - allocator.text("-").append(base) - }; - - match self { - Value::I8(opt_base, value) => { - pretty_internal_signed(opt_base, *value as i64, ConstantType::I8) - } - Value::I16(opt_base, value) => { - pretty_internal_signed(opt_base, *value as i64, ConstantType::I16) - } - Value::I32(opt_base, value) => { - pretty_internal_signed(opt_base, *value as i64, ConstantType::I32) - } - Value::I64(opt_base, value) => { - pretty_internal_signed(opt_base, *value, ConstantType::I64) - } - Value::U8(opt_base, value) => { - pretty_internal(opt_base, *value as u64, ConstantType::U8) - } - Value::U16(opt_base, value) => { - pretty_internal(opt_base, *value as u64, ConstantType::U16) - } - Value::U32(opt_base, value) => { - pretty_internal(opt_base, *value as u64, ConstantType::U32) - } - Value::U64(opt_base, value) => pretty_internal(opt_base, *value, ConstantType::U64), - Value::Void => allocator.text(""), - } - } -} - #[derive(Clone, Debug, Eq, PartialEq)] pub enum Type { Primitive(PrimitiveType), @@ -466,46 +243,6 @@ impl Type { } } -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Type -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { - match self { - Type::Primitive(pt) => allocator.text(format!("{}", pt)), - Type::Function(args, rettype) => { - pretty_comma_separated(allocator, &args.iter().collect()) - .parens() - .append(allocator.space()) - .append(allocator.text("->")) - .append(allocator.space()) - .append(rettype.pretty(allocator)) - } - } - } -} - -impl fmt::Display for Type { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Type::Primitive(pt) => pt.fmt(f), - Type::Function(args, ret) => { - write!(f, "(")?; - let mut argiter = args.iter().peekable(); - while let Some(arg) = argiter.next() { - arg.fmt(f)?; - if argiter.peek().is_some() { - write!(f, ",")?; - } - } - write!(f, "->")?; - ret.fmt(f) - } - } - } -} - impl From for Type { fn from(value: PrimitiveType) -> Self { Type::Primitive(value) @@ -530,55 +267,6 @@ pub enum TypeOrVar { Function(Vec, Box), } -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b TypeOrVar -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { - match self { - TypeOrVar::Primitive(x) => allocator.text(format!("{}", x)), - TypeOrVar::Variable(_, x) => allocator.text(x.to_string()), - TypeOrVar::Function(args, rettype) => { - pretty_comma_separated(allocator, &args.iter().collect()) - .parens() - .append(allocator.space()) - .append(allocator.text("->")) - .append(allocator.space()) - .append(rettype.pretty(allocator)) - } - } - } -} - -impl fmt::Display for TypeOrVar { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - TypeOrVar::Primitive(x) => x.fmt(f), - TypeOrVar::Variable(_, v) => write!(f, "{}", v), - TypeOrVar::Function(args, rettype) => { - write!(f, " write!(f, "()")?, - Some((single, &[])) => { - write!(f, "({})", single)?; - } - Some((last_one, rest)) => { - write!(f, "(")?; - for arg in rest.iter() { - write!(f, "{}, ", arg)?; - } - write!(f, "{})", last_one)?; - } - } - write!(f, "->")?; - rettype.fmt(f)?; - write!(f, ">") - } - } - } -} - impl Default for TypeOrVar { fn default() -> Self { TypeOrVar::new() diff --git a/src/ir/pretty.rs b/src/ir/pretty.rs new file mode 100644 index 0000000..7b948fe --- /dev/null +++ b/src/ir/pretty.rs @@ -0,0 +1,205 @@ +use crate::ir::{Expression, Primitive, Program, TopLevel, Type, TypeOrVar, Value, ValueOrRef}; +use crate::syntax::{self, ConstantType}; +use crate::util::pretty::{Allocator, pretty_function_type, derived_display}; +use pretty::{Arena, DocAllocator, DocBuilder}; + +impl Program { + pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { + allocator + .intersperse( + self.items.iter().map(|x| x.pretty(allocator)), + allocator.line(), + ) + .align() + } +} + +impl TopLevel { + pub fn pretty<'a>( + &self, + allocator: &'a Arena<'a, ()>, + ) -> pretty::DocBuilder<'a, Arena<'a, ()>, ()> { + match self { + TopLevel::Function(name, args, _, expr) => allocator + .text("function") + .append(allocator.space()) + .append(allocator.text(name.as_ref().to_string())) + .append(allocator.space()) + .append( + allocator + .intersperse( + args.iter().map(|(x, _)| allocator.text(x.to_string())), + allocator.text(","), + ) + .parens(), + ) + .append(allocator.space()) + .append(expr.pretty(allocator)), + + TopLevel::Statement(stmt) => stmt.pretty(allocator), + } + } +} + +impl Expression { + pub fn pretty<'a>( + &self, + allocator: &'a Arena<'a, ()>, + ) -> pretty::DocBuilder<'a, Arena<'a, ()>, ()> { + match self { + Expression::Atomic(x) => x.pretty(allocator), + Expression::Cast(_, t, e) => allocator + .text("<") + .append(t.pretty(allocator)) + .append(allocator.text(">")) + .append(e.pretty(allocator)), + Expression::Primitive(_, _, op, exprs) if exprs.len() == 1 => { + op.pretty(allocator).append(exprs[0].pretty(allocator)) + } + Expression::Primitive(_, _, op, exprs) if exprs.len() == 2 => { + let left = exprs[0].pretty(allocator); + let right = exprs[1].pretty(allocator); + + left.append(allocator.space()) + .append(op.pretty(allocator)) + .append(allocator.space()) + .append(right) + .parens() + } + Expression::Primitive(_, _, op, exprs) => { + allocator.text(format!("!!{:?} with {} arguments!!", op, exprs.len())) + } + Expression::Call(_, _, fun, args) => { + let args = args.iter().map(|x| x.pretty(allocator)); + let comma_sepped_args = + allocator.intersperse(args, allocator.text(",")); + fun.pretty(allocator).append(comma_sepped_args.parens()) + } + Expression::Block(_, _, exprs) => match exprs.split_last() { + None => allocator.text("()"), + Some((last, &[])) => last.pretty(allocator), + Some((last, start)) => { + let mut result = allocator.text("{").append(allocator.hardline()); + let starts = start.iter().map(|x| { + x.pretty(allocator) + .append(allocator.text(";")) + .append(allocator.hardline()) + .indent(4) + }); + let last = last + .pretty(allocator) + .append(allocator.hardline()) + .indent(4); + + for start in starts { + result = result.append(start); + } + + result.append(last).append(allocator.text("}")) + } + }, + Expression::Print(_, var) => allocator + .text("print") + .append(allocator.space()) + .append(var.pretty(allocator)), + Expression::Bind(_, var, ty, expr) => allocator + .text(var.as_ref().to_string()) + .append(allocator.space()) + .append(allocator.text(":")) + .append(allocator.space()) + .append(ty.pretty(allocator)) + .append(allocator.space()) + .append(allocator.text("=")) + .append(allocator.space()) + .append(expr.pretty(allocator)), + } + } +} + +impl Primitive { + pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { + match self { + Primitive::Plus => allocator.text("+"), + Primitive::Minus => allocator.text("-"), + Primitive::Times => allocator.text("*"), + Primitive::Divide => allocator.text("/"), + } + } +} + +impl ValueOrRef { + pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { + match self { + ValueOrRef::Value(_, _, v) => v.pretty(allocator), + ValueOrRef::Ref(_, _, v) => allocator.text(v.as_ref().to_string()), + } + } +} + +impl Value { + pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { + let pretty_internal = |opt_base: &Option, x, t| { + syntax::Value::Number(*opt_base, Some(t), x).pretty(allocator) + }; + + let pretty_internal_signed = |opt_base, x: i64, t| { + let base = pretty_internal(opt_base, x.unsigned_abs(), t); + + allocator.text("-").append(base) + }; + + match self { + Value::I8(opt_base, value) => { + pretty_internal_signed(opt_base, *value as i64, ConstantType::I8) + } + Value::I16(opt_base, value) => { + pretty_internal_signed(opt_base, *value as i64, ConstantType::I16) + } + Value::I32(opt_base, value) => { + pretty_internal_signed(opt_base, *value as i64, ConstantType::I32) + } + Value::I64(opt_base, value) => { + pretty_internal_signed(opt_base, *value, ConstantType::I64) + } + Value::U8(opt_base, value) => { + pretty_internal(opt_base, *value as u64, ConstantType::U8) + } + Value::U16(opt_base, value) => { + pretty_internal(opt_base, *value as u64, ConstantType::U16) + } + Value::U32(opt_base, value) => { + pretty_internal(opt_base, *value as u64, ConstantType::U32) + } + Value::U64(opt_base, value) => pretty_internal(opt_base, *value, ConstantType::U64), + Value::Void => allocator.text(""), + } + } +} + +impl Type { + pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { + match self { + Type::Function(args, rettype) => pretty_function_type!(allocator, args, rettype), + Type::Primitive(prim) => prim.pretty(allocator), + } + } +} + +impl TypeOrVar { + pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { + match self { + TypeOrVar::Function(args, rettype) => pretty_function_type!(allocator, args, rettype), + TypeOrVar::Primitive(prim) => prim.pretty(allocator), + TypeOrVar::Variable(_, name) => allocator.text(name.to_string()), + } + } +} + +derived_display!(Program); +derived_display!(TopLevel); +derived_display!(Expression); +derived_display!(Primitive); +derived_display!(Type); +derived_display!(TypeOrVar); +derived_display!(ValueOrRef); +derived_display!(Value); diff --git a/src/repl.rs b/src/repl.rs index 7d90a50..1fbd501 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -106,9 +106,9 @@ impl REPL { pub fn process_input(&mut self, line_no: usize, command: String) { if let Err(err) = self.process(line_no, command) { if let Err(e) = self.emit_diagnostic(Diagnostic::from(err)) { - eprintln!( - "WOAH! System having trouble printing error messages. This is very bad. ({})", - e + tracing::error!( + error = %e, + "WOAH! System having trouble printing error messages. This is very bad.", ); } } diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index 6cafc43..c4c1a56 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -1,14 +1,9 @@ -use crate::syntax::ast::{Expression, Program, Statement, Value}; -use pretty::{DocAllocator, DocBuilder, Pretty}; +use crate::syntax::ast::{ConstantType, Expression, Program, Statement, TopLevel, Value}; +use crate::util::pretty::{Allocator, derived_display}; +use pretty::{DocAllocator, DocBuilder}; -use super::{ConstantType, TopLevel}; - -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Program -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { +impl Program { + pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { let mut result = allocator.nil(); for tl in self.items.iter() { @@ -22,12 +17,8 @@ where } } -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b TopLevel -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { +impl TopLevel { + pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { match self { TopLevel::Statement(stmt) => stmt.pretty(allocator), TopLevel::Function(name, arg_names, body) => allocator @@ -42,7 +33,7 @@ where allocator .intersperse( arg_names.iter().map(|x| allocator.text(x.to_string())), - CommaSep {}, + allocator.text(","), ) .parens(), ) @@ -52,12 +43,8 @@ where } } -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Statement -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { +impl Statement { + pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { match self { Statement::Binding(_, var, expr) => allocator .text(var.to_string()) @@ -73,12 +60,8 @@ where } } -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Expression -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { +impl Expression { + pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { match self { Expression::Value(_, val) => val.pretty(allocator), Expression::Reference(_, var) => allocator.text(var.to_string()), @@ -102,12 +85,12 @@ where Expression::Primitive(_, op, exprs) => { let call = allocator.text(op.to_string()); let args = exprs.iter().map(|x| x.pretty(allocator)); - let comma_sepped_args = allocator.intersperse(args, CommaSep {}); + let comma_sepped_args = allocator.intersperse(args, allocator.text(",")); call.append(comma_sepped_args.parens()) } Expression::Call(_, fun, args) => { let args = args.iter().map(|x| x.pretty(allocator)); - let comma_sepped_args = allocator.intersperse(args, CommaSep {}); + let comma_sepped_args = allocator.intersperse(args, allocator.text(",")); fun.pretty(allocator).append(comma_sepped_args.parens()) } Expression::Block(_, stmts) => match stmts.split_last() { @@ -133,12 +116,8 @@ where } } -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b Value -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { +impl Value { + pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { match self { Value::Number(opt_base, ty, value) => { let value_str = match opt_base { @@ -171,15 +150,8 @@ fn type_suffix(x: &Option) -> &'static str { } } -#[derive(Clone, Copy)] -pub struct CommaSep {} - -impl<'a, D, A> Pretty<'a, D, A> for CommaSep -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> DocBuilder<'a, D, A> { - allocator.text(",").append(allocator.space()) - } -} +derived_display!(Program); +derived_display!(TopLevel); +derived_display!(Statement); +derived_display!(Expression); +derived_display!(Value); \ No newline at end of file diff --git a/src/type_infer/finalize.rs b/src/type_infer/finalize.rs index 59aecf3..3765946 100644 --- a/src/type_infer/finalize.rs +++ b/src/type_infer/finalize.rs @@ -6,24 +6,8 @@ pub fn finalize_program( mut program: Program, resolutions: &TypeResolutions, ) -> Program { - println!("RESOLUTIONS:"); for (name, ty) in resolutions.iter() { - println!("{} => {}", name, ty); - } - println!("PROGRAM:"); - { - use pretty::{DocAllocator, Pretty}; - let allocator = pretty::BoxAllocator; - allocator - .text("---------------") - .append(allocator.hardline()) - .append(program.pretty(&allocator)) - .1 - .render_colored( - 70, - pretty::termcolor::StandardStream::stdout(pretty::termcolor::ColorChoice::Auto), - ) - .expect("rendering works"); + tracing::debug!(name = %name, resolved_type = %ty, "resolved type variable"); } Program { diff --git a/src/util/pretty.rs b/src/util/pretty.rs index 2cfa55e..1816dae 100644 --- a/src/util/pretty.rs +++ b/src/util/pretty.rs @@ -1,49 +1,35 @@ -use internment::ArcIntern; -use pretty::{DocAllocator, Pretty}; +use pretty::Arena; -#[derive(Clone)] -pub struct PrettySymbol { - name: ArcIntern, +pub type Allocator<'a> = Arena<'a, ()>; + +macro_rules! pretty_function_type { + ($allocator: ident, $args: ident, $rettype: ident) => { + $allocator + .intersperse( + $args.iter().map(|x| x.pretty($allocator)), + $allocator.text(","), + ) + .parens() + .append($allocator.space()) + .append($allocator.text("->")) + .append($allocator.space()) + .append($rettype.pretty($allocator)) + }; } -impl<'a> From<&'a ArcIntern> for PrettySymbol { - fn from(value: &'a ArcIntern) -> Self { - PrettySymbol { - name: value.clone(), +macro_rules! derived_display { + ($type: ty) => { + impl std::fmt::Display for $type { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let arena = pretty::Arena::new(); + let doc = self.pretty(&arena); + doc.render_fmt(732, f) + } } - } + }; } -impl<'a, D, A> Pretty<'a, D, A> for PrettySymbol -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { - allocator.text(self.name.as_ref().to_string()) - } -} - -impl<'a, 'b, D, A> Pretty<'a, D, A> for &'b PrettySymbol -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, -{ - fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, A> { - allocator.text(self.name.as_ref().to_string()) - } -} - -#[allow(clippy::ptr_arg)] -pub fn pretty_comma_separated<'a, D, A, P>( - allocator: &'a D, - args: &Vec

, -) -> pretty::DocBuilder<'a, D, A> -where - A: 'a, - D: ?Sized + DocAllocator<'a, A>, - P: Pretty<'a, D, A> + Clone, -{ - let individuals = args.iter().map(|x| x.clone().pretty(allocator)); - allocator.intersperse(individuals, ", ") -} +// this is a dumb Rust trick to export the functions to the rest +// of the crate, but not globally. +pub(crate) use pretty_function_type; +pub(crate) use derived_display; \ No newline at end of file -- 2.53.0 From 745e263b69820a40621d9c0a5b79cd4ae0642c20 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 2 Mar 2024 21:52:56 -0800 Subject: [PATCH 23/59] think that replaces all the printlns I care about --- src/backend/into_crane.rs | 15 +- src/compiler.rs | 16 +- src/ir/pretty.rs | 5 +- src/syntax/pretty.rs | 4 +- src/type_infer/convert.rs | 2 - src/type_infer/finalize.rs | 2 +- src/type_infer/solve.rs | 596 +++---------------------------------- src/util/pretty.rs | 2 +- 8 files changed, 57 insertions(+), 585 deletions(-) diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index b260927..3806c8a 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -260,6 +260,7 @@ impl Backend { /// Compile an expression, returning the Cranelift Value for the expression and /// its type. + #[tracing::instrument(level = "trace", skip(self, variables, builder))] fn compile_expression( &mut self, expr: Expression, @@ -456,10 +457,6 @@ impl Backend { Some(ReferenceBuilder::Global(_, global_value)) => { let pointer = self.module.target_config().pointer_type(); let pointer_to = builder.ins().symbol_value(pointer, *global_value); - println!( - "STORE {}: cranelift_type {} origin type {:?}", - name, value, value_type - ); builder.ins().store(MemFlags::new(), value, pointer_to, 0); Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)) } @@ -529,14 +526,14 @@ impl Backend { /// Compile a value or reference into Cranelift, returning the Cranelift Value for /// the expression and its type. + #[tracing::instrument(level = "trace", skip(self, variables, builder))] fn compile_value_or_ref( &self, - valref: ValueOrRef, + value_or_ref: ValueOrRef, variables: &HashMap, builder: &mut FunctionBuilder, ) -> Result<(entities::Value, ConstantType), BackendError> { - println!("compile_value_or_ref {:?}", valref); - match valref { + match value_or_ref { ValueOrRef::Value(_, _, val) => match val { Value::I8(_, v) => { // Cranelift does a funny thing where it checks you aren't using bits in the I64 @@ -585,10 +582,6 @@ impl Backend { let pointer_to = self.module.target_config().pointer_type(); let pointer_value = builder.ins().symbol_value(pointer_to, *gv); let cranelift_type = ir::Type::from(*ty); - println!( - "READ {}: cranelift_type {} origin type {:?}", - name, cranelift_type, ty - ); let value = builder .ins() diff --git a/src/compiler.rs b/src/compiler.rs index 388fccc..8e8e5ec 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -137,22 +137,12 @@ impl Compiler { let mut backend = Backend::object_file(Triple::host())?; let unknown = "".to_string(); backend.compile_program("gogogo", ir)?; - println!("FINAL MODULE:"); - println!(" FUNCTIONS:"); + for (_, decl) in backend.module.declarations().get_functions() { - println!( - " {}: {:?}", - decl.name.as_ref().unwrap_or(&unknown), - decl.linkage - ); + tracing::debug!(name = %decl.name.as_ref().unwrap_or(&unknown), linkage = ?decl.linkage, "function definition"); } - println!(" DATA:"); for (_, decl) in backend.module.declarations().get_data_objects() { - println!( - " {}: {:?}", - decl.name.as_ref().unwrap_or(&unknown), - decl.linkage - ); + tracing::debug!(name = %decl.name.as_ref().unwrap_or(&unknown), linkage = ?decl.linkage, "data definition"); } Ok(Some(backend.bytes()?)) } diff --git a/src/ir/pretty.rs b/src/ir/pretty.rs index 7b948fe..ae0ce15 100644 --- a/src/ir/pretty.rs +++ b/src/ir/pretty.rs @@ -1,6 +1,6 @@ use crate::ir::{Expression, Primitive, Program, TopLevel, Type, TypeOrVar, Value, ValueOrRef}; use crate::syntax::{self, ConstantType}; -use crate::util::pretty::{Allocator, pretty_function_type, derived_display}; +use crate::util::pretty::{derived_display, pretty_function_type, Allocator}; use pretty::{Arena, DocAllocator, DocBuilder}; impl Program { @@ -71,8 +71,7 @@ impl Expression { } Expression::Call(_, _, fun, args) => { let args = args.iter().map(|x| x.pretty(allocator)); - let comma_sepped_args = - allocator.intersperse(args, allocator.text(",")); + let comma_sepped_args = allocator.intersperse(args, allocator.text(",")); fun.pretty(allocator).append(comma_sepped_args.parens()) } Expression::Block(_, _, exprs) => match exprs.split_last() { diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index c4c1a56..3186b63 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -1,5 +1,5 @@ use crate::syntax::ast::{ConstantType, Expression, Program, Statement, TopLevel, Value}; -use crate::util::pretty::{Allocator, derived_display}; +use crate::util::pretty::{derived_display, Allocator}; use pretty::{DocAllocator, DocBuilder}; impl Program { @@ -154,4 +154,4 @@ derived_display!(Program); derived_display!(TopLevel); derived_display!(Statement); derived_display!(Expression); -derived_display!(Value); \ No newline at end of file +derived_display!(Value); diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 3c11de8..0c2410a 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -150,7 +150,6 @@ fn convert_statement( .get(&final_name) .expect("print variable defined before use") .clone(); - println!("varty for {} is {}", final_name, varty); constraint_db.push(Constraint::Printable(loc.clone(), varty.clone())); @@ -257,7 +256,6 @@ fn convert_expression( .get(&final_name) .cloned() .expect("variable bound before use"); - println!("rtype for {} is {}", final_name, rtype); let refexp = ir::Expression::Atomic(ir::ValueOrRef::Ref(loc, rtype.clone(), final_name)); diff --git a/src/type_infer/finalize.rs b/src/type_infer/finalize.rs index 3765946..883f2a2 100644 --- a/src/type_infer/finalize.rs +++ b/src/type_infer/finalize.rs @@ -101,7 +101,7 @@ fn finalize_type(ty: TypeOrVar, resolutions: &TypeResolutions) -> Type { TypeOrVar::Variable(_, tvar) => match resolutions.get(&tvar) { None => panic!("Did not resolve type for type variable {}", tvar), Some(pt) => { - println!("Finalizing {} to {}", tvar, pt); + tracing::trace!(type_variable = %tvar, final_type = %pt, "finalizing variable type"); pt.clone() } }, diff --git a/src/type_infer/solve.rs b/src/type_infer/solve.rs index fc9b849..2301d1d 100644 --- a/src/type_infer/solve.rs +++ b/src/type_infer/solve.rs @@ -300,16 +300,18 @@ pub fn solve_constraints( ) -> TypeInferenceResult { let mut errors = vec![]; let mut warnings = vec![]; + let mut iteration = 0u64; loop { let mut changed_something = false; let mut all_constraints_solved = true; let mut new_constraints = vec![]; - println!("\n\n\nCONSTRAINT:"); + tracing::debug!(iteration, "Restarting constraint solving loop"); for constraint in constraint_db.iter() { - println!(" {}", constraint); + tracing::debug!(%constraint, "remaining constraint"); } + iteration += 1; while let Some(constraint) = constraint_db.pop() { match constraint { @@ -328,7 +330,7 @@ pub fn solve_constraints( loc, from_type, to_type, )); } - println!("changed something because finished CanCastTo"); + tracing::trace!(form = %from_type, to = %to_type, "changed something because we can determine if we can do the cast"); changed_something = true; } @@ -350,41 +352,44 @@ pub fn solve_constraints( TypeOrVar::Function(args2, ret2), )); } - println!("changed something because finished CanCastTo (2)"); + tracing::trace!("changed something because we transferred CanCastTo to equivalence checks for function types"); changed_something = true; } Constraint::CanCastTo( loc, - TypeOrVar::Function(_, _), + ft @ TypeOrVar::Function(_, _), pt @ TypeOrVar::Primitive(_), ) => { + tracing::trace!(function_type = %ft, primitive_type = %pt, "changed something because we can't cast a function type to a primitive type"); errors.push(TypeInferenceError::CannotCastFromFunctionType(loc, pt)); - println!("changed something because finished CanCastTo (3)"); changed_something = true; } Constraint::CanCastTo( loc, pt @ TypeOrVar::Primitive(_), - TypeOrVar::Function(_, _), + ft @ TypeOrVar::Function(_, _), ) => { + tracing::trace!(function_type = %ft, primitive_type = %pt, "changed something because we can't cast a primitive type to a function type"); errors.push(TypeInferenceError::CannotCastToFunctionType(loc, pt)); changed_something = true; - println!("changed something because finished CanCastTo (4)"); } // if we're testing if an actual primitive type is numeric, that's pretty easy - Constraint::ConstantNumericType(_, TypeOrVar::Primitive(_)) => { + Constraint::ConstantNumericType(loc, TypeOrVar::Primitive(pt)) => { + tracing::trace!(primitive_type = %pt, "changed something because its easy to tell if a constant number can be a primitive type"); + if pt.max_value().is_none() { + errors.push(TypeInferenceError::NotANumber(loc, pt)) + } changed_something = true; - println!("changed something because constant numeric type (1)"); } // if we're testing if a function type is numeric, then throw a useful warning Constraint::ConstantNumericType(loc, t @ TypeOrVar::Function(_, _)) => { + tracing::trace!(function_type = %t, "changed something because functions can't be constant numbers"); errors.push(TypeInferenceError::CannotMakeNumberAFunction(loc, t, None)); changed_something = true; - println!("changed something because constant numeric type (2)"); } // if we're testing if a number can fit into a numeric type, we can just do that! @@ -399,67 +404,70 @@ pub fn solve_constraints( Some(_) => {} } changed_something = true; - println!("changed something because fits in numeric type (1)"); + tracing::trace!(primitive_type = %ctype, value = val, "changed something because we can test for a value fitting in a primitive type"); } // if we're testing if a function type can fit into a numeric type, that's a problem Constraint::FitsInNumType(loc, t @ TypeOrVar::Function(_, _), val) => { + tracing::trace!(function_type = %t, "changed something because values don't fit in function types"); errors.push(TypeInferenceError::CannotMakeNumberAFunction( loc, t, Some(val), )); changed_something = true; - println!("changed something because fits in numeric type (2)"); } // if we want to know if a type is something, and it is something, then we're done - Constraint::IsSomething(_, TypeOrVar::Function(_, _)) - | Constraint::IsSomething(_, TypeOrVar::Primitive(_)) => { + Constraint::IsSomething(_, t @ TypeOrVar::Function(_, _)) + | Constraint::IsSomething(_, t @ TypeOrVar::Primitive(_)) => { + tracing::trace!(tested_type = %t, "changed something because type is definitely something"); changed_something = true; - println!("changed something because 1"); } // if we want to know if something is signed, we can check its primitive type Constraint::IsSigned(loc, TypeOrVar::Primitive(pt)) => { + tracing::trace!(primitive_type = %pt, "changed something because we can check if a primitive is signed"); if !pt.valid_operators().contains(&("-", 1)) { errors.push(TypeInferenceError::IsNotSigned(loc, pt)); } changed_something = true; - println!("changed something because 2"); } // again with the functions and the numbers Constraint::IsSigned(loc, t @ TypeOrVar::Function(_, _)) => { + tracing::trace!(function_type = %t, "changed something because functions are not signed"); errors.push(TypeInferenceError::CannotCastFromFunctionType(loc, t)); changed_something = true; - println!("changed something because 3"); } // if we're testing if an actual primitive type is numeric, that's pretty easy - Constraint::NumericType(_, TypeOrVar::Primitive(_)) => { + Constraint::NumericType(loc, TypeOrVar::Primitive(pt)) => { + tracing::trace!(primitive_type = %pt, "changed something because its easy to tell if a primitive type is numeric"); + if pt.max_value().is_none() { + errors.push(TypeInferenceError::NotANumber(loc, pt)) + } changed_something = true; - println!("changed something because 4"); } // if we're testing if a function type is numeric, then throw a useful warning Constraint::NumericType(loc, t @ TypeOrVar::Function(_, _)) => { + tracing::trace!(function_type = %t, "changed something because function types aren't numeric"); errors.push(TypeInferenceError::CannotMakeNumberAFunction(loc, t, None)); changed_something = true; - println!("changed something because 5"); } // all of our primitive types are printable - Constraint::Printable(_, TypeOrVar::Primitive(_)) => { + Constraint::Printable(_, TypeOrVar::Primitive(pt)) => { + tracing::trace!(primitive_type = %pt, "changed something because primitive types are printable"); changed_something = true; - println!("changed something because 6"); } // function types are definitely not printable - Constraint::Printable(loc, TypeOrVar::Function(_, _)) => { + Constraint::Printable(loc, ft @ TypeOrVar::Function(_, _)) => { + tracing::trace!(function_type = %ft, "changed something because function types are not printable"); errors.push(TypeInferenceError::FunctionsAreNotPrintable(loc)); changed_something = true; - println!("changed something because 7"); } Constraint::ProperPrimitiveArgs(loc, prim, mut args, ret) => match prim { @@ -478,7 +486,7 @@ pub fn solve_constraints( new_constraints.push(Constraint::Equivalent(loc.clone(), left, ret)); changed_something = true; all_constraints_solved = false; - println!("changed something because 8"); + tracing::trace!(primitive = %prim, "changed something because we expanded out a binary primitive operation"); } Primitive::Minus if args.len() == 1 => { @@ -488,7 +496,7 @@ pub fn solve_constraints( new_constraints.push(Constraint::Equivalent(loc, value, ret)); changed_something = true; all_constraints_solved = false; - println!("changed something because 9"); + tracing::trace!(primitive = %prim, "changed something because we expanded out a unary primitive operation"); } Primitive::Plus | Primitive::Times | Primitive::Divide => { @@ -500,7 +508,7 @@ pub fn solve_constraints( args.len(), )); changed_something = true; - println!("changed something because 10"); + tracing::trace!(primitive = %prim, provided_arity = args.len(), "changed something because binary primitive operation arity is wrong"); } Primitive::Minus => { @@ -512,7 +520,7 @@ pub fn solve_constraints( args.len(), )); changed_something = true; - println!("changed something because 11"); + tracing::trace!(primitive = %prim, provided_arity = args.len(), "changed something because unary primitive operation arity is wrong"); } }, @@ -530,7 +538,7 @@ pub fn solve_constraints( )); } changed_something = true; - println!("changed something because 12"); + tracing::trace!(primitive_type1 = %pt1, primitive_type2 = %pt2, "changed something because we checked for primitive type equivalence"); } Constraint::Equivalent( @@ -543,9 +551,9 @@ pub fn solve_constraints( ft @ TypeOrVar::Function(_, _), pt @ TypeOrVar::Primitive(_), ) => { + tracing::trace!(primitive_type = %pt, function_type = %ft, "changed something because function and primitive types cannot be equivalent"); errors.push(TypeInferenceError::NotEquivalent(loc, pt, ft)); changed_something = true; - println!("changed something because 13"); } Constraint::Equivalent( @@ -553,6 +561,7 @@ pub fn solve_constraints( TypeOrVar::Variable(_, name1), TypeOrVar::Variable(_, name2), ) if name1 == name2 => { + tracing::trace!(name = %name1, "changed something because variable is equivalent to itself"); changed_something = true; } @@ -574,7 +583,7 @@ pub fn solve_constraints( } changed_something = true; - println!("changed something because 14"); + tracing::trace!("changed something because we checked/rewrote if function types are equivalent"); } Constraint::Equivalent(_, TypeOrVar::Variable(_, ref name), ref rhs) => { @@ -582,16 +591,16 @@ pub fn solve_constraints( changed_something |= replace_variable(&mut new_constraints, name, rhs); all_constraints_solved &= rhs.is_resolved(); if changed_something { - println!("changed something because 15, maybe (name is {})", name); + tracing::trace!(%name, new_type = %rhs, "changed something because we were able to rewrite name somewhere"); } new_constraints.push(constraint); } Constraint::Equivalent(loc, lhs, rhs @ TypeOrVar::Variable(_, _)) => { + tracing::trace!(new_left = %rhs, new_right = %lhs, "changed something because we flipped the order on an equivalence"); new_constraints.push(Constraint::Equivalent(loc, rhs, lhs)); changed_something = true; all_constraints_solved = false; - println!("changed something because 16"); } Constraint::CanCastTo(_, TypeOrVar::Variable(_, _), _) @@ -636,7 +645,7 @@ pub fn solve_constraints( resty.clone(), )); warnings.push(TypeInferenceWarning::DefaultedTo(loc.clone(), resty)); - println!("Adding number equivalence"); + tracing::trace!("Adding number equivalence"); false } else { true @@ -679,520 +688,3 @@ fn replace_variable( changed_anything } - -/// Solve all the constraints in the provided database. -/// -/// This process can take a bit, so you might not want to do it multiple times. Basically, -/// it's going to grind on these constraints until either it figures them out, or it stops -/// making progress. I haven't done the math on the constraints to even figure out if this -/// is guaranteed to halt, though, let alone terminate in some reasonable amount of time. -/// -/// The return value is a type inference result, which pairs some warnings with either a -/// successful set of type resolutions (mappings from type variables to their values), or -/// a series of inference errors. -pub fn _solve_constraints_old( - mut constraint_db: Vec, -) -> TypeInferenceResult { - let mut errors = vec![]; - let mut warnings = vec![]; - let mut resolutions: HashMap, Type> = HashMap::new(); - let mut changed_something = true; - - // We want to run this inference endlessly, until either we have solved all of our - // constraints. Internal to the loop, we have a check that will make sure that we - // do (eventually) stop. - while changed_something && !constraint_db.is_empty() { - println!("\n\n\nCONSTRAINT:"); - for constraint in constraint_db.iter() { - println!(" {}", constraint); - } - println!("RESOLUTIONS:"); - for (name, ty) in resolutions.iter() { - println!(" {} = {}", name, ty); - } - // Set this to false at the top of the loop. We'll set this to true if we make - // progress in any way further down, but having this here prevents us from going - // into an infinite look when we can't figure stuff out. - changed_something = false; - // This is sort of a double-buffering thing; we're going to rename constraint_db - // and then set it to a new empty vector, which we'll add to as we find new - // constraints or find ourselves unable to solve existing ones. - let mut local_constraints = constraint_db; - constraint_db = vec![]; - - // OK. First thing we're going to do is run through all of our constraints, - // and see if we can solve any, or reduce them to theoretically more simple - // constraints. - for constraint in local_constraints.drain(..) { - match constraint { - // Currently, all of our types are printable - Constraint::Printable(_loc, _ty) => changed_something = true, - - // If we're looking for a type to be something (anything!), and it's not a type - // variable, then yay, we've solved it. - Constraint::IsSomething(_, TypeOrVar::Function(_, _)) - | Constraint::IsSomething(_, TypeOrVar::Primitive(_)) => changed_something = true, - - // Otherwise, see if we've resolved this variable to anything. If not, add it - // back. - Constraint::IsSomething(_, TypeOrVar::Variable(_, ref name)) => { - if resolutions.get(name).is_none() { - constraint_db.push(constraint); - } else { - changed_something = true; - } - } - - // Case #1a: We have two primitive types. If they're equal, we've discharged this - // constraint! We can just continue. If they're not equal, add an error and then - // see what else we come up with. - Constraint::Equivalent( - loc, - a @ TypeOrVar::Primitive(_), - b @ TypeOrVar::Primitive(_), - ) => { - if a != b { - errors.push(TypeInferenceError::NotEquivalent(loc, a, b)); - } - changed_something = true; - } - - // Case #2: They're both variables. In which case, we'll have to do much the same - // check, but now on their resolutions. - Constraint::Equivalent( - ref loc, - TypeOrVar::Variable(_, ref name1), - TypeOrVar::Variable(_, ref name2), - ) => match (resolutions.get(name1), resolutions.get(name2)) { - (None, None) => { - constraint_db.push(constraint); - } - (Some(pt), None) => { - resolutions.insert(name2.clone(), pt.clone()); - changed_something = true; - } - (None, Some(pt)) => { - resolutions.insert(name1.clone(), pt.clone()); - changed_something = true; - } - (Some(pt1), Some(pt2)) if pt1 == pt2 => { - changed_something = true; - } - (Some(pt1), Some(pt2)) => { - errors.push(TypeInferenceError::NotEquivalent( - loc.clone(), - pt1.clone().into(), - pt2.clone().into(), - )); - changed_something = true; - } - }, - - // Case #3: One of the two constraints is a primitive, and the other is a variable. - // In this case, we'll check to see if we've resolved the variable, and check for - // equivalence if we have. If we haven't, we'll set that variable to be primitive - // type. - Constraint::Equivalent(loc, t, TypeOrVar::Variable(vloc, name)) - | Constraint::Equivalent(loc, TypeOrVar::Variable(vloc, name), t) => { - println!("IN THIS CASE with {}", name); - match resolutions.get(&name) { - None => match t.try_into() { - Ok(real_type) => { - println!(" HERE with {} and {}", name, real_type); - resolutions.insert(name, real_type); - } - Err(variable_type) => { - println!( - " REJECTED INTO RETURN with {} and {}", - name, variable_type - ); - constraint_db.push(Constraint::Equivalent( - loc, - variable_type, - TypeOrVar::Variable(vloc, name), - )); - continue; - } - }, - Some(t2) if &t == t2 => { - println!(" MATCHED at {} == {}", t, t2); - } - Some(t2) => errors.push(TypeInferenceError::NotEquivalent( - loc, - t, - t2.clone().into(), - )), - } - changed_something = true; - } - - // Case #4: Like primitives, but for function types. This is a little complicated, because - // we first want to resolve all the type variables in the two types, and then see if they're - // equivalent. Fortunately, though, we can cheat a bit. What we're going to do is first see - // if the two types have the same arity (the same number of arguments). If not, we know the - // types don't match. If they do, then we're going to just turn this into a bunch of different - // equivalence constraints by matching up each of the arguments as well as the return type, and - // then restarting the type checking loop. That will cause any of those type variables to be - // handled appropriately. This even works recursively, so we can support arbitrarily nested - // function types. - Constraint::Equivalent( - loc, - ref a @ TypeOrVar::Function(ref args1, ref ret1), - ref b @ TypeOrVar::Function(ref args2, ref ret2), - ) => { - if args1.len() != args2.len() { - errors.push(TypeInferenceError::NotEquivalent( - loc.clone(), - a.clone(), - b.clone(), - )); - } else { - for (left, right) in args1.iter().zip(args2.iter()) { - constraint_db.push(Constraint::Equivalent( - loc.clone(), - left.clone(), - right.clone(), - )); - } - } - - constraint_db.push(Constraint::Equivalent( - loc, - ret1.as_ref().clone(), - ret2.as_ref().clone(), - )); - - changed_something = true; - } - - // Case #5: They're just totally the wrong types. In which case, we're done with - // this one; emit the error and drop the constraint. - Constraint::Equivalent(loc, a, b) => { - errors.push(TypeInferenceError::NotEquivalent(loc, a, b)); - changed_something = true; - } - - // Make sure that the provided number fits within the provided constant type. For the - // moment, we're going to call an error here a failure, although this could be a - // warning in the future. - Constraint::FitsInNumType(loc, TypeOrVar::Primitive(ctype), val) => { - match ctype.max_value() { - None => { - errors.push(TypeInferenceError::NotANumber(loc, ctype)); - } - Some(max_value) if max_value < val => { - errors.push(TypeInferenceError::ConstantTooLarge(loc, ctype, val)); - } - Some(_) => {} - }; - - changed_something = true; - } - - // If we have a non-constant type, then let's see if we can advance this to a constant - // type - Constraint::FitsInNumType(loc, TypeOrVar::Variable(vloc, var), val) => { - match resolutions.get(&var) { - None => constraint_db.push(Constraint::FitsInNumType( - loc, - TypeOrVar::Variable(vloc, var), - val, - )), - Some(nt) => { - constraint_db.push(Constraint::FitsInNumType( - loc, - nt.clone().into(), - val, - )); - changed_something = true; - } - } - } - - // Function types definitely do not fit in numeric types - Constraint::FitsInNumType(loc, t @ TypeOrVar::Function(_, _), val) => { - errors.push(TypeInferenceError::CannotMakeNumberAFunction( - loc, - t.clone(), - Some(val), - )); - } - - // If the left type in a "can cast to" check is a variable, let's see if we can advance - // it into something more tangible - Constraint::CanCastTo(loc, TypeOrVar::Variable(vloc, var), to_type) => { - match resolutions.get(&var) { - None => constraint_db.push(Constraint::CanCastTo( - loc, - TypeOrVar::Variable(vloc, var), - to_type, - )), - Some(nt) => { - constraint_db.push(Constraint::CanCastTo( - loc, - nt.clone().into(), - to_type, - )); - changed_something = true; - } - } - } - - // If the right type in a "can cast to" check is a variable, same deal - Constraint::CanCastTo(loc, from_type, TypeOrVar::Variable(vloc, var)) => { - match resolutions.get(&var) { - None => constraint_db.push(Constraint::CanCastTo( - loc, - from_type, - TypeOrVar::Variable(vloc, var), - )), - Some(nt) => { - constraint_db.push(Constraint::CanCastTo( - loc, - from_type, - nt.clone().into(), - )); - changed_something = true; - } - } - } - - // If both of them are types, then we can actually do the test. yay! - Constraint::CanCastTo( - loc, - TypeOrVar::Primitive(from_type), - TypeOrVar::Primitive(to_type), - ) => { - if !from_type.can_cast_to(&to_type) { - errors.push(TypeInferenceError::CannotSafelyCast( - loc, from_type, to_type, - )); - } - changed_something = true; - } - - // If either type is a function type, then we can only cast if the two types - // are equivalent. - Constraint::CanCastTo( - loc, - t1 @ TypeOrVar::Function(_, _), - t2 @ TypeOrVar::Function(_, _), - ) => { - if t1 != t2 { - errors.push(TypeInferenceError::CannotCastBetweenFunctinoTypes( - loc, - t1.clone(), - t2.clone(), - )); - } - changed_something = true; - } - - Constraint::CanCastTo( - loc, - t @ TypeOrVar::Function(_, _), - TypeOrVar::Primitive(_), - ) => { - errors.push(TypeInferenceError::CannotCastFromFunctionType( - loc, - t.clone(), - )); - changed_something = true; - } - - Constraint::CanCastTo( - loc, - TypeOrVar::Primitive(_), - t @ TypeOrVar::Function(_, _), - ) => { - errors.push(TypeInferenceError::CannotCastToFunctionType(loc, t.clone())); - changed_something = true; - } - - // As per usual, if we're trying to test if a type variable is numeric, first - // we try to advance it to a primitive - Constraint::NumericType(loc, TypeOrVar::Variable(vloc, var)) => { - match resolutions.get(&var) { - None => constraint_db - .push(Constraint::NumericType(loc, TypeOrVar::Variable(vloc, var))), - Some(nt) => { - constraint_db.push(Constraint::NumericType(loc, nt.clone().into())); - changed_something = true; - } - } - } - - // Of course, if we get to a primitive type, then it's true, because all of our - // primitive types are numbers - Constraint::NumericType(_, TypeOrVar::Primitive(_)) => { - changed_something = true; - } - - // But functions are definitely not numbers - Constraint::NumericType(loc, t @ TypeOrVar::Function(_, _)) => { - errors.push(TypeInferenceError::CannotMakeNumberAFunction( - loc, - t.clone(), - None, - )); - changed_something = true; - } - - // As per usual, if we're trying to test if a type variable is numeric, first - // we try to advance it to a primitive - Constraint::ConstantNumericType(loc, TypeOrVar::Variable(vloc, var)) => { - match resolutions.get(&var) { - None => constraint_db.push(Constraint::ConstantNumericType( - loc, - TypeOrVar::Variable(vloc, var), - )), - Some(nt) => { - constraint_db - .push(Constraint::ConstantNumericType(loc, nt.clone().into())); - changed_something = true; - } - } - } - - // Of course, if we get to a primitive type, then it's true, because all of our - // primitive types are numbers - Constraint::ConstantNumericType(_, TypeOrVar::Primitive(_)) => { - changed_something = true; - } - - // But functions are definitely not numbers - Constraint::ConstantNumericType(loc, t @ TypeOrVar::Function(_, _)) => { - errors.push(TypeInferenceError::CannotMakeNumberAFunction( - loc, - t.clone(), - None, - )); - changed_something = true; - } - - // OK, this one could be a little tricky if we tried to do it all at once, but - // instead what we're going to do here is just use this constraint to generate - // a bunch more constraints, and then go have the engine solve those. The only - // real errors we're going to come up with here are "arity errors"; errors we - // find by discovering that the number of arguments provided doesn't make sense - // given the primitive being used. - Constraint::ProperPrimitiveArgs(loc, prim, mut args, ret) => match prim { - Primitive::Plus | Primitive::Times | Primitive::Divide if args.len() != 2 => { - errors.push(TypeInferenceError::WrongPrimitiveArity( - loc, - prim, - 2, - 2, - args.len(), - )); - changed_something = true; - } - - Primitive::Plus | Primitive::Times | Primitive::Divide => { - let right = args.pop().expect("2 > 0"); - let left = args.pop().expect("2 > 1"); - - // technically testing that both are numeric is redundant, but it might give - // a slightly helpful type error if we do both. - constraint_db.push(Constraint::NumericType(loc.clone(), left.clone())); - constraint_db.push(Constraint::NumericType(loc.clone(), right.clone())); - constraint_db.push(Constraint::NumericType(loc.clone(), ret.clone())); - constraint_db.push(Constraint::Equivalent( - loc.clone(), - left.clone(), - right, - )); - constraint_db.push(Constraint::Equivalent(loc, left, ret)); - changed_something = true; - } - - Primitive::Minus if args.is_empty() || args.len() > 2 => { - errors.push(TypeInferenceError::WrongPrimitiveArity( - loc, - prim, - 1, - 2, - args.len(), - )); - changed_something = true; - } - - Primitive::Minus if args.len() == 1 => { - let arg = args.pop().expect("1 > 0"); - constraint_db.push(Constraint::NumericType(loc.clone(), arg.clone())); - constraint_db.push(Constraint::NumericType(loc.clone(), ret.clone())); - constraint_db.push(Constraint::Equivalent(loc, arg, ret)); - changed_something = true; - } - - Primitive::Minus => { - let right = args.pop().expect("2 > 0"); - let left = args.pop().expect("2 > 1"); - - // technically testing that both are numeric is redundant, but it might give - // a slightly helpful type error if we do both. - constraint_db.push(Constraint::NumericType(loc.clone(), left.clone())); - constraint_db.push(Constraint::NumericType(loc.clone(), right.clone())); - constraint_db.push(Constraint::NumericType(loc.clone(), ret.clone())); - constraint_db.push(Constraint::Equivalent( - loc.clone(), - left.clone(), - right, - )); - constraint_db.push(Constraint::Equivalent(loc.clone(), left, ret)); - changed_something = true; - } - }, - - _ => panic!("unexpected constraint"), - } - } - - // If that didn't actually come up with anything, and we just recycled all the constraints - // back into the database unchanged, then let's take a look for cases in which we just - // wanted something we didn't know to be a number. Basically, those are cases where the - // user just wrote a number, but didn't tell us what type it was, and there isn't enough - // information in the context to tell us. If that happens, we'll just set that type to - // be u64, and warn the user that we did so. - if !changed_something && !constraint_db.is_empty() { - local_constraints = constraint_db; - constraint_db = vec![]; - - for constraint in local_constraints.drain(..) { - match constraint { - Constraint::ConstantNumericType(loc, t @ TypeOrVar::Variable(_, _)) => { - let resty = TypeOrVar::Primitive(PrimitiveType::U64); - constraint_db.push(Constraint::Equivalent( - loc.clone(), - t, - TypeOrVar::Primitive(PrimitiveType::U64), - )); - warnings.push(TypeInferenceWarning::DefaultedTo(loc, resty)); - changed_something = true; - } - - _ => constraint_db.push(constraint), - } - } - } - } - - // OK, we left our loop. Which means that either we solved everything, or we didn't. - // If we didn't, turn the unsolved constraints into type inference errors, and add - // them to our error list. - let mut unsolved_constraint_errors = constraint_db - .drain(..) - .map(TypeInferenceError::CouldNotSolve) - .collect(); - errors.append(&mut unsolved_constraint_errors); - - // How'd we do? - if errors.is_empty() { - TypeInferenceResult::Success { - result: resolutions, - warnings, - } - } else { - TypeInferenceResult::Failure { errors, warnings } - } -} diff --git a/src/util/pretty.rs b/src/util/pretty.rs index 1816dae..88b2789 100644 --- a/src/util/pretty.rs +++ b/src/util/pretty.rs @@ -31,5 +31,5 @@ macro_rules! derived_display { // this is a dumb Rust trick to export the functions to the rest // of the crate, but not globally. +pub(crate) use derived_display; pub(crate) use pretty_function_type; -pub(crate) use derived_display; \ No newline at end of file -- 2.53.0 From d54680a8fed4cccf25f90cc229e4ac3dbfdc95e2 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 2 Mar 2024 21:54:11 -0800 Subject: [PATCH 24/59] shorten some logs --- src/type_infer/solve.rs | 50 ++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/type_infer/solve.rs b/src/type_infer/solve.rs index 2301d1d..73d85b5 100644 --- a/src/type_infer/solve.rs +++ b/src/type_infer/solve.rs @@ -330,7 +330,7 @@ pub fn solve_constraints( loc, from_type, to_type, )); } - tracing::trace!(form = %from_type, to = %to_type, "changed something because we can determine if we can do the cast"); + tracing::trace!(form = %from_type, to = %to_type, "we can determine if we can do the cast"); changed_something = true; } @@ -352,7 +352,7 @@ pub fn solve_constraints( TypeOrVar::Function(args2, ret2), )); } - tracing::trace!("changed something because we transferred CanCastTo to equivalence checks for function types"); + tracing::trace!("we transferred CanCastTo to equivalence checks for function types"); changed_something = true; } @@ -361,7 +361,7 @@ pub fn solve_constraints( ft @ TypeOrVar::Function(_, _), pt @ TypeOrVar::Primitive(_), ) => { - tracing::trace!(function_type = %ft, primitive_type = %pt, "changed something because we can't cast a function type to a primitive type"); + tracing::trace!(function_type = %ft, primitive_type = %pt, "we can't cast a function type to a primitive type"); errors.push(TypeInferenceError::CannotCastFromFunctionType(loc, pt)); changed_something = true; } @@ -371,14 +371,14 @@ pub fn solve_constraints( pt @ TypeOrVar::Primitive(_), ft @ TypeOrVar::Function(_, _), ) => { - tracing::trace!(function_type = %ft, primitive_type = %pt, "changed something because we can't cast a primitive type to a function type"); + tracing::trace!(function_type = %ft, primitive_type = %pt, "we can't cast a primitive type to a function type"); errors.push(TypeInferenceError::CannotCastToFunctionType(loc, pt)); changed_something = true; } // if we're testing if an actual primitive type is numeric, that's pretty easy Constraint::ConstantNumericType(loc, TypeOrVar::Primitive(pt)) => { - tracing::trace!(primitive_type = %pt, "changed something because its easy to tell if a constant number can be a primitive type"); + tracing::trace!(primitive_type = %pt, "its easy to tell if a constant number can be a primitive type"); if pt.max_value().is_none() { errors.push(TypeInferenceError::NotANumber(loc, pt)) } @@ -387,7 +387,7 @@ pub fn solve_constraints( // if we're testing if a function type is numeric, then throw a useful warning Constraint::ConstantNumericType(loc, t @ TypeOrVar::Function(_, _)) => { - tracing::trace!(function_type = %t, "changed something because functions can't be constant numbers"); + tracing::trace!(function_type = %t, "functions can't be constant numbers"); errors.push(TypeInferenceError::CannotMakeNumberAFunction(loc, t, None)); changed_something = true; } @@ -404,12 +404,12 @@ pub fn solve_constraints( Some(_) => {} } changed_something = true; - tracing::trace!(primitive_type = %ctype, value = val, "changed something because we can test for a value fitting in a primitive type"); + tracing::trace!(primitive_type = %ctype, value = val, "we can test for a value fitting in a primitive type"); } // if we're testing if a function type can fit into a numeric type, that's a problem Constraint::FitsInNumType(loc, t @ TypeOrVar::Function(_, _), val) => { - tracing::trace!(function_type = %t, "changed something because values don't fit in function types"); + tracing::trace!(function_type = %t, "values don't fit in function types"); errors.push(TypeInferenceError::CannotMakeNumberAFunction( loc, t, @@ -421,13 +421,13 @@ pub fn solve_constraints( // if we want to know if a type is something, and it is something, then we're done Constraint::IsSomething(_, t @ TypeOrVar::Function(_, _)) | Constraint::IsSomething(_, t @ TypeOrVar::Primitive(_)) => { - tracing::trace!(tested_type = %t, "changed something because type is definitely something"); + tracing::trace!(tested_type = %t, "type is definitely something"); changed_something = true; } // if we want to know if something is signed, we can check its primitive type Constraint::IsSigned(loc, TypeOrVar::Primitive(pt)) => { - tracing::trace!(primitive_type = %pt, "changed something because we can check if a primitive is signed"); + tracing::trace!(primitive_type = %pt, "we can check if a primitive is signed"); if !pt.valid_operators().contains(&("-", 1)) { errors.push(TypeInferenceError::IsNotSigned(loc, pt)); } @@ -436,14 +436,14 @@ pub fn solve_constraints( // again with the functions and the numbers Constraint::IsSigned(loc, t @ TypeOrVar::Function(_, _)) => { - tracing::trace!(function_type = %t, "changed something because functions are not signed"); + tracing::trace!(function_type = %t, "functions are not signed"); errors.push(TypeInferenceError::CannotCastFromFunctionType(loc, t)); changed_something = true; } // if we're testing if an actual primitive type is numeric, that's pretty easy Constraint::NumericType(loc, TypeOrVar::Primitive(pt)) => { - tracing::trace!(primitive_type = %pt, "changed something because its easy to tell if a primitive type is numeric"); + tracing::trace!(primitive_type = %pt, "its easy to tell if a primitive type is numeric"); if pt.max_value().is_none() { errors.push(TypeInferenceError::NotANumber(loc, pt)) } @@ -452,20 +452,20 @@ pub fn solve_constraints( // if we're testing if a function type is numeric, then throw a useful warning Constraint::NumericType(loc, t @ TypeOrVar::Function(_, _)) => { - tracing::trace!(function_type = %t, "changed something because function types aren't numeric"); + tracing::trace!(function_type = %t, "function types aren't numeric"); errors.push(TypeInferenceError::CannotMakeNumberAFunction(loc, t, None)); changed_something = true; } // all of our primitive types are printable Constraint::Printable(_, TypeOrVar::Primitive(pt)) => { - tracing::trace!(primitive_type = %pt, "changed something because primitive types are printable"); + tracing::trace!(primitive_type = %pt, "primitive types are printable"); changed_something = true; } // function types are definitely not printable Constraint::Printable(loc, ft @ TypeOrVar::Function(_, _)) => { - tracing::trace!(function_type = %ft, "changed something because function types are not printable"); + tracing::trace!(function_type = %ft, "function types are not printable"); errors.push(TypeInferenceError::FunctionsAreNotPrintable(loc)); changed_something = true; } @@ -486,7 +486,7 @@ pub fn solve_constraints( new_constraints.push(Constraint::Equivalent(loc.clone(), left, ret)); changed_something = true; all_constraints_solved = false; - tracing::trace!(primitive = %prim, "changed something because we expanded out a binary primitive operation"); + tracing::trace!(primitive = %prim, "we expanded out a binary primitive operation"); } Primitive::Minus if args.len() == 1 => { @@ -496,7 +496,7 @@ pub fn solve_constraints( new_constraints.push(Constraint::Equivalent(loc, value, ret)); changed_something = true; all_constraints_solved = false; - tracing::trace!(primitive = %prim, "changed something because we expanded out a unary primitive operation"); + tracing::trace!(primitive = %prim, "we expanded out a unary primitive operation"); } Primitive::Plus | Primitive::Times | Primitive::Divide => { @@ -508,7 +508,7 @@ pub fn solve_constraints( args.len(), )); changed_something = true; - tracing::trace!(primitive = %prim, provided_arity = args.len(), "changed something because binary primitive operation arity is wrong"); + tracing::trace!(primitive = %prim, provided_arity = args.len(), "binary primitive operation arity is wrong"); } Primitive::Minus => { @@ -520,7 +520,7 @@ pub fn solve_constraints( args.len(), )); changed_something = true; - tracing::trace!(primitive = %prim, provided_arity = args.len(), "changed something because unary primitive operation arity is wrong"); + tracing::trace!(primitive = %prim, provided_arity = args.len(), "unary primitive operation arity is wrong"); } }, @@ -538,7 +538,7 @@ pub fn solve_constraints( )); } changed_something = true; - tracing::trace!(primitive_type1 = %pt1, primitive_type2 = %pt2, "changed something because we checked for primitive type equivalence"); + tracing::trace!(primitive_type1 = %pt1, primitive_type2 = %pt2, "we checked for primitive type equivalence"); } Constraint::Equivalent( @@ -551,7 +551,7 @@ pub fn solve_constraints( ft @ TypeOrVar::Function(_, _), pt @ TypeOrVar::Primitive(_), ) => { - tracing::trace!(primitive_type = %pt, function_type = %ft, "changed something because function and primitive types cannot be equivalent"); + tracing::trace!(primitive_type = %pt, function_type = %ft, "function and primitive types cannot be equivalent"); errors.push(TypeInferenceError::NotEquivalent(loc, pt, ft)); changed_something = true; } @@ -561,7 +561,7 @@ pub fn solve_constraints( TypeOrVar::Variable(_, name1), TypeOrVar::Variable(_, name2), ) if name1 == name2 => { - tracing::trace!(name = %name1, "changed something because variable is equivalent to itself"); + tracing::trace!(name = %name1, "variable is equivalent to itself"); changed_something = true; } @@ -583,7 +583,7 @@ pub fn solve_constraints( } changed_something = true; - tracing::trace!("changed something because we checked/rewrote if function types are equivalent"); + tracing::trace!("we checked/rewrote if function types are equivalent"); } Constraint::Equivalent(_, TypeOrVar::Variable(_, ref name), ref rhs) => { @@ -591,13 +591,13 @@ pub fn solve_constraints( changed_something |= replace_variable(&mut new_constraints, name, rhs); all_constraints_solved &= rhs.is_resolved(); if changed_something { - tracing::trace!(%name, new_type = %rhs, "changed something because we were able to rewrite name somewhere"); + tracing::trace!(%name, new_type = %rhs, "we were able to rewrite name somewhere"); } new_constraints.push(constraint); } Constraint::Equivalent(loc, lhs, rhs @ TypeOrVar::Variable(_, _)) => { - tracing::trace!(new_left = %rhs, new_right = %lhs, "changed something because we flipped the order on an equivalence"); + tracing::trace!(new_left = %rhs, new_right = %lhs, "we flipped the order on an equivalence"); new_constraints.push(Constraint::Equivalent(loc, rhs, lhs)); changed_something = true; all_constraints_solved = false; -- 2.53.0 From 50f224ab2e2d2fac51e66c6ca4c8f905190af7b2 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 2 Mar 2024 21:56:25 -0800 Subject: [PATCH 25/59] Formatting --- src/type_infer/solve.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/type_infer/solve.rs b/src/type_infer/solve.rs index 73d85b5..82123b3 100644 --- a/src/type_infer/solve.rs +++ b/src/type_infer/solve.rs @@ -352,7 +352,9 @@ pub fn solve_constraints( TypeOrVar::Function(args2, ret2), )); } - tracing::trace!("we transferred CanCastTo to equivalence checks for function types"); + tracing::trace!( + "we transferred CanCastTo to equivalence checks for function types" + ); changed_something = true; } -- 2.53.0 From b9da300db233828df637fe059af99cd055114fdd Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 2 Mar 2024 21:57:46 -0800 Subject: [PATCH 26/59] unneeded import --- src/syntax.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/syntax.rs b/src/syntax.rs index b532f02..3f89769 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -47,7 +47,7 @@ pub use crate::syntax::location::Location; pub use crate::syntax::parser::{ProgramParser, TopLevelParser}; pub use crate::syntax::tokens::{LexerError, Token}; #[cfg(test)] -use ::pretty::{Arena, Pretty}; +use ::pretty::Arena; use lalrpop_util::ParseError; #[cfg(test)] use proptest::{arbitrary::Arbitrary, prop_assert, prop_assert_eq}; -- 2.53.0 From 09308649ed9a10727b6e543d76f311eaae70afc8 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 2 Mar 2024 22:05:53 -0800 Subject: [PATCH 27/59] remove dead test cases --- src/type_infer/convert.rs | 141 +------------------------------------- 1 file changed, 1 insertion(+), 140 deletions(-) diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 0c2410a..80a572b 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -436,143 +436,4 @@ fn merge_prereq(left: &mut Vec, prereq: Option) { if let Some(item) = prereq { left.push(item) } -} - -#[cfg(test)] -mod tests { - // use super::*; - // use crate::syntax::Location; - // - // fn one() -> syntax::Expression { - // syntax::Expression::Value( - // Location::manufactured(), - // syntax::Value::Number(None, None, 1), - // ) - // } - // - // fn vec_contains bool>(x: &[T], f: F) -> bool { - // for x in x.iter() { - // if f(x) { - // return true; - // } - // } - // false - // } - // - // fn infer_expression( - // x: syntax::Expression, - // ) -> (ir::Expression, Vec, Vec, Type) { - // let mut constraints = Vec::new(); - // let renames = HashMap::new(); - // let mut bindings = HashMap::new(); - // let (stmts, expr, ty) = convert_expression(x, &mut constraints, &renames, &mut bindings); - // (expr, stmts, constraints, ty) - // } - // - // fn infer_top_level(x: syntax::TopLevel) -> (Vec, Vec) { - // let mut constraints = Vec::new(); - // let mut renames = HashMap::new(); - // let mut bindings = HashMap::new(); - // let res = convert_top_level(x, &mut constraints, &mut renames, &mut bindings); - // (res, constraints) - // } - // - // #[test] - // fn constant_one() { - // let (expr, stmts, constraints, ty) = infer_expression(one()); - // assert!(stmts.is_empty()); - // assert!(matches!( - // expr, - // ir::Expression::Atomic(ir::ValueOrRef::Value(_, _, ir::Value::Unknown(None, 1))) - // )); - // assert!(vec_contains(&constraints, |x| matches!( - // x, - // Constraint::FitsInNumType(_, _, 1) - // ))); - // assert!(vec_contains( - // &constraints, - // |x| matches!(x, Constraint::ConstantNumericType(_, t) if t == &ty) - // )); - // } - // - // #[test] - // fn one_plus_one() { - // let opo = syntax::Expression::Primitive( - // Location::manufactured(), - // "+".to_string(), - // vec![one(), one()], - // ); - // let (expr, stmts, constraints, ty) = infer_expression(opo); - // assert!(stmts.is_empty()); - // assert!(matches!(expr, ir::Expression::Primitive(_, t, ir::Primitive::Plus, _) if t == ty)); - // assert!(vec_contains(&constraints, |x| matches!( - // x, - // Constraint::FitsInNumType(_, _, 1) - // ))); - // assert!(vec_contains( - // &constraints, - // |x| matches!(x, Constraint::ConstantNumericType(_, t) if t != &ty) - // )); - // assert!(vec_contains( - // &constraints, - // |x| matches!(x, Constraint::ProperPrimitiveArgs(_, ir::Primitive::Plus, args, ret) if args.len() == 2 && ret == &ty) - // )); - // } - // - // #[test] - // fn one_plus_one_plus_one() { - // let stmt = syntax::TopLevel::parse(1, "x = 1 + 1 + 1;").expect("basic parse"); - // let (stmts, constraints) = infer_top_level(stmt); - // assert_eq!(stmts.len(), 2); - // let ir::TopLevel::Statement(ir::Statement::Binding( - // _args, - // name1, - // temp_ty1, - // ir::Expression::Primitive(_, primty1, ir::Primitive::Plus, primargs1), - // )) = stmts.get(0).expect("item two") - // else { - // panic!("Failed to match first statement"); - // }; - // let ir::TopLevel::Statement(ir::Statement::Binding( - // _args, - // name2, - // temp_ty2, - // ir::Expression::Primitive(_, primty2, ir::Primitive::Plus, primargs2), - // )) = stmts.get(1).expect("item two") - // else { - // panic!("Failed to match second statement"); - // }; - // let &[ir::ValueOrRef::Value(_, ref left1ty, _), ir::ValueOrRef::Value(_, ref right1ty, _)] = - // &primargs1[..] - // else { - // panic!("Failed to match first arguments"); - // }; - // let &[ir::ValueOrRef::Ref(_, _, ref left2name), ir::ValueOrRef::Value(_, ref right2ty, _)] = - // &primargs2[..] - // else { - // panic!("Failed to match first arguments"); - // }; - // assert_ne!(name1, name2); - // assert_ne!(temp_ty1, temp_ty2); - // assert_ne!(primty1, primty2); - // assert_eq!(name1, left2name); - // assert!(vec_contains( - // &constraints, - // |x| matches!(x, Constraint::ConstantNumericType(_, t) if t == left1ty) - // )); - // assert!(vec_contains( - // &constraints, - // |x| matches!(x, Constraint::ConstantNumericType(_, t) if t == right1ty) - // )); - // assert!(vec_contains( - // &constraints, - // |x| matches!(x, Constraint::ConstantNumericType(_, t) if t == right2ty) - // )); - // for (i, s) in stmts.iter().enumerate() { - // println!("{}: {:?}", i, s); - // } - // for (i, c) in constraints.iter().enumerate() { - // println!("{}: {:?}", i, c); - // } - // } -} +} \ No newline at end of file -- 2.53.0 From 08d41686be58f253ded06bc2b9f36dd683a3b5ea Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 2 Mar 2024 22:08:00 -0800 Subject: [PATCH 28/59] cute unicode tricks --- src/syntax/parser.lalrpop | 1 + src/syntax/tokens.rs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index 03ac419..cb5d37c 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -45,6 +45,7 @@ extern { "-" => Token::Operator('-'), "*" => Token::Operator('*'), "/" => Token::Operator('/'), + "÷" => Token::Operator('/'), // the previous items just match their tokens, and if you try // to name and use "their value", you get their source location. diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs index 48c1c52..0b0e486 100644 --- a/src/syntax/tokens.rs +++ b/src/syntax/tokens.rs @@ -60,6 +60,7 @@ pub enum Token { #[token("}")] CloseBrace, + #[token("λ")] #[token("lambda")] #[token("function")] Function, @@ -72,7 +73,7 @@ pub enum Token { // Next are the operators for NGR. We only have 4, now, but // we might extend these later, or even make them user-definable! - #[regex(r"[+\-*/]", |v| v.slice().chars().next())] + #[regex(r"[+\-*/÷]", |v| v.slice().chars().next())] Operator(char), /// Numbers capture both the value we read from the input, -- 2.53.0 From 2b8133f4db38edbf359a3f1238ed225f2eeab8c5 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Tue, 5 Mar 2024 20:11:17 -0800 Subject: [PATCH 29/59] upgrade dependencies --- Cargo.toml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ad4b96b..82314ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,14 +12,14 @@ path = "src/lib.rs" clap = { version = "4.5.1", features = ["derive"] } codespan = "0.11.1" codespan-reporting = "0.11.1" -cranelift-codegen = "0.105.1" -cranelift-jit = "0.105.1" -cranelift-frontend = "0.105.1" -cranelift-module = "0.105.1" -cranelift-native = "0.105.1" -cranelift-object = "0.105.1" +cranelift-codegen = "0.105.2" +cranelift-jit = "0.105.2" +cranelift-frontend = "0.105.2" +cranelift-module = "0.105.2" +cranelift-native = "0.105.2" +cranelift-object = "0.105.2" internment = { version = "0.7.4", default-features = false, features = ["arc"] } -lalrpop-util = "0.20.0" +lalrpop-util = "0.20.2" lazy_static = "1.4.0" logos = "0.14.0" pretty = { version = "0.12.3", features = ["termcolor"] } @@ -27,11 +27,11 @@ proptest = "1.4.0" rand = "0.8.5" rustyline = "13.0.0" target-lexicon = "0.12.14" -tempfile = "3.10.0" +tempfile = "3.10.1" thiserror = "1.0.57" anyhow = "1.0.80" tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["time", "json", "env-filter"] } [build-dependencies] -lalrpop = "0.20.0" +lalrpop = "0.20.2" -- 2.53.0 From 8dab797c90d6b9f69db774e8da7341c42836741f Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Tue, 5 Mar 2024 20:25:29 -0800 Subject: [PATCH 30/59] Formatting --- src/type_infer/convert.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 80a572b..0d5c2a8 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -436,4 +436,4 @@ fn merge_prereq(left: &mut Vec, prereq: Option) { if let Some(item) = prereq { left.push(item) } -} \ No newline at end of file +} -- 2.53.0 From ac564e6e41647c405a8ca3b66ca9bd3917cfc033 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Wed, 6 Mar 2024 09:50:42 -0800 Subject: [PATCH 31/59] Add support for syntax blocks. --- examples/basic/function0001.ngr | 2 +- examples/basic/function0002.ngr | 2 +- examples/basic/function0003.ngr | 11 +++++++++++ examples/basic/generated0001.ngr | 2 +- examples/basic/generated0003.ngr | 2 +- src/syntax/ast.rs | 5 +++++ src/syntax/eval.rs | 2 ++ src/syntax/parser.lalrpop | 6 +++++- src/syntax/pretty.rs | 1 + src/syntax/validate.rs | 7 +++++++ src/type_infer/convert.rs | 4 ++++ 11 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 examples/basic/function0003.ngr diff --git a/examples/basic/function0001.ngr b/examples/basic/function0001.ngr index 6cdb5c3..ebd955d 100644 --- a/examples/basic/function0001.ngr +++ b/examples/basic/function0001.ngr @@ -1,5 +1,5 @@ x = 1; -function add_x(y) x + y +function add_x(y) x + y; a = 3; print x; result = add_x(a); diff --git a/examples/basic/function0002.ngr b/examples/basic/function0002.ngr index 53a0bac..2ff6935 100644 --- a/examples/basic/function0002.ngr +++ b/examples/basic/function0002.ngr @@ -1,5 +1,5 @@ x = 1; -function add_x(y) x + y +function add_x(y) x + y; a = 3; function add_x_twice(y) add_x(y) + x print x; diff --git a/examples/basic/function0003.ngr b/examples/basic/function0003.ngr new file mode 100644 index 0000000..0b55b08 --- /dev/null +++ b/examples/basic/function0003.ngr @@ -0,0 +1,11 @@ +x = 1u64; +function mean_x(y) { + base = x + y; + result = base / 2; + result; +}; +a = 3; +mean_x_and_a = mean_x(a); +mean_x_and_9 = mean_x(9); +print mean_x_and_a; +print mean_x_and_9; \ No newline at end of file diff --git a/examples/basic/generated0001.ngr b/examples/basic/generated0001.ngr index ae46375..3f4ffad 100644 --- a/examples/basic/generated0001.ngr +++ b/examples/basic/generated0001.ngr @@ -1,3 +1,3 @@ x = 4u64; -function f(y) (x + y) +function f(y) (x + y); print x; \ No newline at end of file diff --git a/examples/basic/generated0003.ngr b/examples/basic/generated0003.ngr index 616bba6..c43d413 100644 --- a/examples/basic/generated0003.ngr +++ b/examples/basic/generated0003.ngr @@ -1,4 +1,4 @@ n = (49u8 + 155u8); q = n; -function u (b) n + b +function u (b) n + b; v = n; \ No newline at end of file diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 89928e8..5a947fe 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -95,6 +95,7 @@ impl fmt::Display for Name { pub enum Statement { Binding(Location, Name, Expression), Print(Location, Name), + Expression(Expression), } impl PartialEq for Statement { @@ -108,6 +109,10 @@ impl PartialEq for Statement { Statement::Print(_, name2) => name1 == name2, _ => false, }, + Statement::Expression(e1) => match other { + Statement::Expression(e2) => e1 == e2, + _ => false, + }, } } } diff --git a/src/syntax/eval.rs b/src/syntax/eval.rs index e84a0b9..98f482c 100644 --- a/src/syntax/eval.rs +++ b/src/syntax/eval.rs @@ -69,6 +69,8 @@ impl Statement { stdout.push_str(&line); Ok(Value::Void) } + + Statement::Expression(e) => e.eval(stdout, env), } } } diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index cb5d37c..9798ed1 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -77,7 +77,7 @@ pub TopLevel: TopLevel = { } Function: TopLevel = { - "function" "(" OptionalComma ")" => + "function" "(" OptionalComma ")" ";" => TopLevel::Function(opt_name, args, exp), } @@ -154,6 +154,10 @@ Statement: Statement = { Location::new(file_idx, ls..le), Name::new(v, Location::new(file_idx, name_start..name_end)), ), + + // A statement can just be an expression. + ";" => + Statement::Expression(e), } // Expressions! Expressions are a little fiddly, because we're going to diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index 3186b63..740021e 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -56,6 +56,7 @@ impl Statement { .text("print") .append(allocator.space()) .append(allocator.text(var.to_string())), + Statement::Expression(e) => e.pretty(allocator), } } } diff --git a/src/syntax/validate.rs b/src/syntax/validate.rs index 9c89885..de7dbee 100644 --- a/src/syntax/validate.rs +++ b/src/syntax/validate.rs @@ -177,6 +177,13 @@ impl Statement { Statement::Print(loc, var) => { errors.push(Error::UnboundVariable(loc.clone(), var.to_string())) } + + Statement::Expression(e) => { + let (mut exp_errors, mut exp_warnings) = e.validate(bound_variables); + + errors.append(&mut exp_errors); + warnings.append(&mut exp_warnings); + } } (errors, warnings) diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 0d5c2a8..01b6955 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -163,6 +163,10 @@ fn convert_statement( ir::Expression::Bind(loc, final_name, ty, Box::new(expr)) } + + syntax::Statement::Expression(e) => { + convert_expression(e, constraint_db, renames, bindings).0 + } } } -- 2.53.0 From 6c3fc2de01fb4ab853a0f5f1c15ff2f5c93fbb1d Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Wed, 6 Mar 2024 14:35:46 -0800 Subject: [PATCH 32/59] parse tweaks --- examples/basic/function0002.ngr | 2 +- examples/basic/function0003.ngr | 2 +- src/syntax/parser.lalrpop | 40 +++++++++++++-------------------- src/syntax/validate.rs | 1 + 4 files changed, 19 insertions(+), 26 deletions(-) diff --git a/examples/basic/function0002.ngr b/examples/basic/function0002.ngr index 2ff6935..69e6e72 100644 --- a/examples/basic/function0002.ngr +++ b/examples/basic/function0002.ngr @@ -1,7 +1,7 @@ x = 1; function add_x(y) x + y; a = 3; -function add_x_twice(y) add_x(y) + x +function add_x_twice(y) add_x(y) + x; print x; result = add_x(a); print x; diff --git a/examples/basic/function0003.ngr b/examples/basic/function0003.ngr index 0b55b08..1813dd8 100644 --- a/examples/basic/function0003.ngr +++ b/examples/basic/function0003.ngr @@ -2,7 +2,7 @@ x = 1u64; function mean_x(y) { base = x + y; result = base / 2; - result; + result }; a = 3; mean_x_and_a = mean_x(a); diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index 9798ed1..ee546b9 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -60,7 +60,8 @@ pub Program: Program = { // a program is just a set of statements => Program { items - } + }, + => Program { items: vec![] }, } ProgramTopLevel: Vec = { @@ -68,17 +69,21 @@ ProgramTopLevel: Vec = { rest.push(t); rest }, - => Vec::new(), + => vec![t], } pub TopLevel: TopLevel = { => f, - => TopLevel::Statement(s), + ";" => TopLevel::Statement(s), } Function: TopLevel = { "function" "(" OptionalComma ")" ";" => TopLevel::Function(opt_name, args, exp), +// "function" "(" OptionalComma ")" "{" "}" => +// TopLevel::Function(opt_name, args, Expression::Block(Location::new(file_idx, s..e), vec![])), +// "function" "(" OptionalComma ")" "{" "}" => +// TopLevel::Function(opt_name, args, Expression::Block(Location::new(file_idx, s..e), stmts)), } OptionalName: Option = { @@ -112,36 +117,22 @@ Statements: Vec = { // a statement is either a set of statements followed by another // statement (note, here, that you can name the result of a sub-parse // using ) ... - => { + ";" => { stmts.push(stmt); stmts }, - // ... or it's nothing. This may feel like an awkward way to define - // lists of things -- and it is a bit awkward -- but there are actual - // technical reasons that you want to (a) use recursivion to define - // these, and (b) use *left* recursion, specifically. That's why, in - // this file, all of the recursive cases are to the left, like they - // are above. - // - // the details of why left recursion is better is actually pretty - // fiddly and in the weeds, and if you're interested you should look - // up LALR parsers versus LL parsers; both their differences and how - // they're constructed, as they're kind of neat. - // - // but if you're just writing grammars with lalrpop, then you should - // just remember that you should always use left recursion, and be - // done with it. - => { - Vec::new() + => { + vec![stmt] } } +#[inline] Statement: Statement = { // A statement can be a variable binding. Note, here, that we use this // funny @L thing to get the source location before the variable, so that // we can say that this statement spans across everything. - "> "=" ";" => + "> "=" => Statement::Binding( Location::new(file_idx, ls..le), Name::new(v, Location::new(file_idx, ls..var_end)), @@ -149,14 +140,14 @@ Statement: Statement = { ), // A statement can just be a print statement. - "print" "> ";" => + "print" "> => Statement::Print( Location::new(file_idx, ls..le), Name::new(v, Location::new(file_idx, name_start..name_end)), ), // A statement can just be an expression. - ";" => + => Statement::Expression(e), } @@ -237,6 +228,7 @@ AtomicExpression: Expression = { "> => Expression::Value(Location::new(file_idx, l..end), Value::Number(n.0, n.1, n.2)), // this expression could actually be a block! "{" "}" => Expression::Block(Location::new(file_idx, s..e), stmts), + "{" "}" => Expression::Block(Location::new(file_idx, s..e), vec![]), // finally, let people parenthesize expressions and get back to a // lower precedence "(" ")" => e, diff --git a/src/syntax/validate.rs b/src/syntax/validate.rs index de7dbee..9013ac4 100644 --- a/src/syntax/validate.rs +++ b/src/syntax/validate.rs @@ -68,6 +68,7 @@ impl Program { /// actually a problem. pub fn validate(&self) -> (Vec, Vec) { let mut bound_variables = ScopedMap::new(); + println!("validate: {}", self); self.validate_with_bindings(&mut bound_variables) } -- 2.53.0 From 77c4277625543c3a8473be95e4d7271067643aea Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Wed, 6 Mar 2024 18:30:01 -0800 Subject: [PATCH 33/59] that example isn't broken --- examples/basic/{broken1.ngr => math0001.ngr} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename examples/basic/{broken1.ngr => math0001.ngr} (65%) diff --git a/examples/basic/broken1.ngr b/examples/basic/math0001.ngr similarity index 65% rename from examples/basic/broken1.ngr rename to examples/basic/math0001.ngr index 775e24a..b8d66e1 100644 --- a/examples/basic/broken1.ngr +++ b/examples/basic/math0001.ngr @@ -1,4 +1,4 @@ x = 5; -x = 4*x + 3; +y = 4*x + 3; print x; print y; -- 2.53.0 From b0cc2fc26b533fdfa06f67b78ca092d26f15b0c1 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Fri, 8 Mar 2024 18:39:13 -0700 Subject: [PATCH 34/59] Broken structure now gets through syntax evaluator. --- examples/basic/broken0001.ngr | 25 ++++++++ src/eval.rs | 18 ++++++ src/eval/primop.rs | 2 +- src/eval/primtype.rs | 5 ++ src/eval/value.rs | 33 ++++++++++ src/syntax/ast.rs | 26 +++++++- src/syntax/eval.rs | 45 +++++++++++++- src/syntax/parser.lalrpop | 113 +++++++++++++++++++++++----------- src/syntax/pretty.rs | 111 ++++++++++++++++++++++++++++++++- src/syntax/tokens.rs | 26 ++++++++ src/syntax/validate.rs | 22 +++++-- src/type_infer/convert.rs | 13 +++- 12 files changed, 389 insertions(+), 50 deletions(-) create mode 100644 examples/basic/broken0001.ngr diff --git a/examples/basic/broken0001.ngr b/examples/basic/broken0001.ngr new file mode 100644 index 0000000..360380a --- /dev/null +++ b/examples/basic/broken0001.ngr @@ -0,0 +1,25 @@ +struct Point { + x: u64; + y: u64; +} + +function getX(p: Point) -> u64 + p.x; + +function getY(p: Point) -> u64 + p.y; + +function newPoint(x, y) -> Point + Point { + x: x; + y: y; + }; + +function slope(p1, p2) -> u64 + (getY(p2) - p1.y) / (getX(p2) - p1.x); + +origin = newPoint(0, 0); +farther = newPoint(4, 4); + +mySlope = slope(origin, farther); +print mySlope; \ No newline at end of file diff --git a/src/eval.rs b/src/eval.rs index 182f640..daa2b4b 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -86,6 +86,14 @@ pub enum EvalError { usize, usize, ), + #[error("Value has no fields {1} (attempt to get field {2} at {0:?})")] + NoFieldForValue(crate::syntax::Location, Value, ArcIntern), + #[error("Bad field {2} for structure {1:?} at {0:?}")] + BadFieldForStructure( + crate::syntax::Location, + Option>, + ArcIntern, + ), } impl PartialEq> for EvalError { @@ -149,6 +157,16 @@ impl PartialEq> for EvalError { EvalError::WrongArgCount(w, x, y, z) => a == w && b == x && c == y && d == z, _ => false, }, + + EvalError::NoFieldForValue(a, b, c) => match other { + EvalError::NoFieldForValue(x, y, z) => a == x && b == y && c == z, + _ => false, + }, + + EvalError::BadFieldForStructure(a, b, c) => match other { + EvalError::BadFieldForStructure(x, y, z) => a == x && b == y && c == z, + _ => false, + }, } } } diff --git a/src/eval/primop.rs b/src/eval/primop.rs index 322459f..aeecb57 100644 --- a/src/eval/primop.rs +++ b/src/eval/primop.rs @@ -192,7 +192,7 @@ impl Value { right.clone(), )), }, - Value::Closure(_, _, _, _) | Value::Void => { + Value::Closure(_, _, _, _) | Value::Structure(_, _) | Value::Void => { Err(PrimOpError::BadTypeFor(operation.to_string(), left.clone())) } } diff --git a/src/eval/primtype.rs b/src/eval/primtype.rs index f67768e..0d9abc6 100644 --- a/src/eval/primtype.rs +++ b/src/eval/primtype.rs @@ -46,6 +46,8 @@ impl Display for PrimitiveType { pub enum ValuePrimitiveTypeError { #[error("Could not convert function value to primitive type (possible function name: {0:?}")] CannotConvertFunction(Option), + #[error("Could not convert structure value to primitive type (possible function name: {0:?}")] + CannotConvertStructure(Option), } impl<'a, IR> TryFrom<&'a Value> for PrimitiveType { @@ -67,6 +69,9 @@ impl<'a, IR> TryFrom<&'a Value> for PrimitiveType { Value::Closure(name, _, _, _) => Err(ValuePrimitiveTypeError::CannotConvertFunction( name.as_ref().map(|x| (**x).clone()), )), + Value::Structure(name, _) => Err(ValuePrimitiveTypeError::CannotConvertStructure( + name.as_ref().map(|x| (**x).clone()), + )), } } } diff --git a/src/eval/value.rs b/src/eval/value.rs index e3b8230..da54910 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -1,5 +1,6 @@ use crate::util::scoped_map::ScopedMap; use internment::ArcIntern; +use std::collections::HashMap; use std::fmt; /// Values in the interpreter. @@ -26,6 +27,10 @@ pub enum Value { Vec>, IR, ), + Structure( + Option>, + HashMap, Value>, + ), } impl Value { @@ -50,6 +55,10 @@ impl Value { let new_env = env.clone().map_values(|x| x.strip()); Value::Closure(name.clone(), new_env, args.clone(), ()) } + Value::Structure(name, fields) => Value::Structure( + name.clone(), + fields.iter().map(|(n, v)| (n.clone(), v.strip())).collect(), + ), } } } @@ -68,6 +77,16 @@ fn format_value(value: &Value, f: &mut fmt::Formatter<'_>) -> fmt::Resul Value::Number(x) => write!(f, "{}", x), Value::Closure(Some(name), _, _, _) => write!(f, "", name), Value::Closure(None, _, _, _) => write!(f, ""), + Value::Structure(on, fields) => { + if let Some(n) = on { + write!(f, "{}", n.as_str())?; + } + write!(f, "{{")?; + for (n, v) in fields.iter() { + write!(f, " {}: {},", n, v)?; + } + write!(f, " }}") + } } } @@ -132,6 +151,20 @@ impl PartialEq> for Value { _ => false, }, Value::Closure(_, _, _, _) => false, + Value::Structure(on1, fields1) => match other { + Value::Structure(on2, fields2) => { + on1 == on2 && { + let left = fields1.keys().all(|x| fields2.contains_key(x)); + let right = fields2.keys().all(|x| fields1.contains_key(x)); + + left && right + && fields1 + .iter() + .all(|(k, v)| fields2.get(k).map(|v2| v == v2).unwrap_or(false)) + } + } + _ => false, + }, } } } diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 5a947fe..92733df 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -27,7 +27,13 @@ pub struct Program { #[derive(Clone, Debug, PartialEq)] pub enum TopLevel { Statement(Statement), - Function(Option, Vec, Expression), + Function( + Option, + Vec<(Name, Option)>, + Option, + Expression, + ), + Structure(Location, Option, Vec<(Name, Type)>), } /// A Name. @@ -126,7 +132,9 @@ impl PartialEq for Statement { #[derive(Clone, Debug)] pub enum Expression { Value(Location, Value), + Constructor(Location, Name, Vec<(Name, Expression)>), Reference(Location, String), + FieldRef(Location, Box, Name), Cast(Location, String, Box), Primitive(Location, String, Vec), Call(Location, Box, Vec), @@ -140,10 +148,18 @@ impl PartialEq for Expression { Expression::Value(_, val2) => val1 == val2, _ => false, }, + Expression::Constructor(_, name1, fields1) => match other { + Expression::Constructor(_, name2, fields2) => name1 == name2 && fields1 == fields2, + _ => false, + }, Expression::Reference(_, var1) => match other { Expression::Reference(_, var2) => var1 == var2, _ => false, }, + Expression::FieldRef(_, exp1, field1) => match other { + Expression::FieldRef(_, exp2, field2) => exp1 == exp2 && field1 == field2, + _ => false, + }, Expression::Cast(_, t1, e1) => match other { Expression::Cast(_, t2, e2) => t1 == t2 && e1 == e2, _ => false, @@ -169,7 +185,9 @@ impl Expression { pub fn location(&self) -> &Location { match self { Expression::Value(loc, _) => loc, + Expression::Constructor(loc, _, _) => loc, Expression::Reference(loc, _) => loc, + Expression::FieldRef(loc, _, _) => loc, Expression::Cast(loc, _, _) => loc, Expression::Primitive(loc, _, _) => loc, Expression::Call(loc, _, _) => loc, @@ -190,3 +208,9 @@ pub enum Value { /// number at a later time. Number(Option, Option, u64), } + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Type { + Named(Name), + Struct(Option, Vec<(Option, Option)>), +} diff --git a/src/syntax/eval.rs b/src/syntax/eval.rs index 98f482c..506ed13 100644 --- a/src/syntax/eval.rs +++ b/src/syntax/eval.rs @@ -2,6 +2,7 @@ use crate::eval::{EvalError, PrimitiveType, Value}; use crate::syntax::{ConstantType, Expression, Name, Program, Statement, TopLevel}; use crate::util::scoped_map::ScopedMap; use internment::ArcIntern; +use std::collections::HashMap; use std::str::FromStr; impl Program { @@ -23,11 +24,15 @@ impl Program { for stmt in self.items.iter() { match stmt { - TopLevel::Function(name, arg_names, body) => { + TopLevel::Function(name, arg_names, _, body) => { last_result = Value::Closure( name.clone().map(Name::intern), env.clone(), - arg_names.iter().cloned().map(Name::intern).collect(), + arg_names + .iter() + .cloned() + .map(|(x, _)| Name::intern(x)) + .collect(), body.clone(), ); if let Some(name) = name { @@ -36,6 +41,10 @@ impl Program { } TopLevel::Statement(stmt) => last_result = stmt.eval(&mut stdout, &mut env)?, + + TopLevel::Structure(_, _, _) => { + last_result = Value::Void; + } } } @@ -98,11 +107,43 @@ impl Expression { }, }, + Expression::Constructor(_, on, fields) => { + let mut map = HashMap::with_capacity(fields.len()); + + for (k, v) in fields.iter() { + map.insert(k.clone().intern(), v.eval(stdout, env)?); + } + + Ok(Value::Structure(Some(on.clone().intern()), map)) + } + Expression::Reference(loc, n) => env .get(&ArcIntern::new(n.clone())) .ok_or_else(|| EvalError::LookupFailed(loc.clone(), n.clone())) .cloned(), + Expression::FieldRef(loc, expr, field) => { + let struck = expr.eval(stdout, env)?; + + if let Value::Structure(on, mut fields) = struck { + if let Some(value) = fields.remove(&field.clone().intern()) { + Ok(value) + } else { + Err(EvalError::BadFieldForStructure( + loc.clone(), + on, + field.clone().intern(), + )) + } + } else { + Err(EvalError::NoFieldForValue( + loc.clone(), + struck, + field.clone().intern(), + )) + } + } + Expression::Cast(_, target, expr) => { let target_type = PrimitiveType::from_str(target)?; let value = expr.eval(stdout, env)?; diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index ee546b9..e93e29f 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -9,7 +9,7 @@ //! eventually want to leave lalrpop behind.) //! use crate::syntax::{Location, ParserError}; -use crate::syntax::ast::{Program,TopLevel,Statement,Expression,Value,Name}; +use crate::syntax::ast::{Program,TopLevel,Statement,Expression,Value,Name,Type}; use crate::syntax::tokens::{ConstantType, Token}; use internment::ArcIntern; @@ -29,16 +29,21 @@ extern { // here we redeclare all of the tokens. enum Token { "=" => Token::Equals, + ":" => Token::Colon, ";" => Token::Semi, "," => Token::Comma, + "." => Token::Dot, "(" => Token::LeftParen, ")" => Token::RightParen, "<" => Token::LessThan, ">" => Token::GreaterThan, + "_" => Token::Underscore, "{" => Token::OpenBrace, "}" => Token::CloseBrace, + "->" => Token::SingleArrow, "function" => Token::Function, + "struct" => Token::Struct, "print" => Token::Print, "+" => Token::Operator('+'), @@ -53,6 +58,7 @@ extern { // which is why we put their types in angle brackets. "" => Token::Number((>,>,)), "" => Token::Variable(>), + "" => Token::TypeName(>), } } @@ -74,38 +80,18 @@ ProgramTopLevel: Vec = { pub TopLevel: TopLevel = { => f, + => s, ";" => TopLevel::Statement(s), } Function: TopLevel = { - "function" "(" OptionalComma ")" ";" => - TopLevel::Function(opt_name, args, exp), -// "function" "(" OptionalComma ")" "{" "}" => -// TopLevel::Function(opt_name, args, Expression::Block(Location::new(file_idx, s..e), vec![])), -// "function" "(" OptionalComma ")" "{" "}" => -// TopLevel::Function(opt_name, args, Expression::Block(Location::new(file_idx, s..e), stmts)), + "function" "(" > ")" " Type)?> ";" => + TopLevel::Function(opt_name, args, ret.map(|x| x.1), exp), } -OptionalName: Option = { - "> => - Some(Name::new(v, Location::new(file_idx, name_start..name_end))), - => None, -} - -Arguments: Vec = { - "," => { - args.push(arg); - args - }, - - => vec![arg], - - => Vec::new(), -} - -Argument: Name = { - "> => - Name::new(v, Location::new(file_idx, name_start..name_end)), +Argument: (Name, Option) = { + "> => + (Name::new(v, Location::new(file_idx, name_start..name_end)), t.map(|v| v.1)), } OptionalComma: () = { @@ -113,6 +99,40 @@ OptionalComma: () = { "," => (), } +Structure: TopLevel = { + "struct" "{" "}" => { + TopLevel::Structure(Location::new(file_idx, s..e), on, fields) + } +} + +Field: (Name, Type) = { + "> ":" ";" => + (Name::new(name, Location::new(file_idx, s..e)), field_type) +} + +Type: Type = { + => Type::Named(name), + => Type::Named(t), + "struct" "{" "}" => + Type::Struct(on, fields), +} + +TypeField: (Option, Option) = { + ":" ";" => (Some(name), Some(ty)), + (":" "_")? ";" => (Some(name), None), + "_" ":" ";" => (None, Some(ty)), +} + +Name: Name = { + "> => + Name::new(v, Location::new(file_idx, name_start..name_end)), +} + +TypeName: Name = { + "> => + Name::new(v, Location::new(file_idx, name_start..name_end)), +} + Statements: Vec = { // a statement is either a set of statements followed by another // statement (note, here, that you can name the result of a sub-parse @@ -175,9 +195,19 @@ Statement: Statement = { // parse something like "1 + 2 * 3", for example, versus "1 + 2 + 3" or // "1 * 2 + 3", and hopefully that'll help. Expression: Expression = { + ConstructorExpression, AdditiveExpression, } +ConstructorExpression: Expression = { + "{" "}" => + Expression::Constructor(Location::new(file_idx, s..e), name, fields), +} + +FieldSetter: (Name, Expression) = { + ":" ";" => (name, expr), +} + // we group addition and subtraction under the heading "additive" AdditiveExpression: Expression = { "+" => @@ -205,18 +235,15 @@ UnaryExpression: Expression = { } CallExpression: Expression = { - "(" ")" => + "(" > ")" => Expression::Call(Location::new(file_idx, s..e), Box::new(f), args), - AtomicExpression, + FieldExpression, } -CallArguments: Vec = { - => vec![], - => vec![e], - "," => { - args.push(e); - args - } +FieldExpression: Expression = { + "." => + Expression::FieldRef(Location::new(file_idx, s..e), Box::new(fe), field), + AtomicExpression, } // finally, we describe our lowest-level expressions as "atomic", because @@ -232,4 +259,16 @@ AtomicExpression: Expression = { // finally, let people parenthesize expressions and get back to a // lower precedence "(" ")" => e, -} \ No newline at end of file +} + +// Lifted from the LALRPop book, a comma-separated list of T that may or +// may not conclude with a comma. +Comma: Vec = { + ",")*> => match e { + None => v, + Some(e) => { + v.push(e); + v + } + } +}; \ No newline at end of file diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index 740021e..ea306b6 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -1,4 +1,4 @@ -use crate::syntax::ast::{ConstantType, Expression, Program, Statement, TopLevel, Value}; +use crate::syntax::ast::{ConstantType, Expression, Program, Statement, TopLevel, Type, Value}; use crate::util::pretty::{derived_display, Allocator}; use pretty::{DocAllocator, DocBuilder}; @@ -21,7 +21,7 @@ impl TopLevel { pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { match self { TopLevel::Statement(stmt) => stmt.pretty(allocator), - TopLevel::Function(name, arg_names, body) => allocator + TopLevel::Function(name, args, rettype, body) => allocator .text("function") .append(allocator.space()) .append( @@ -32,13 +32,61 @@ impl TopLevel { .append( allocator .intersperse( - arg_names.iter().map(|x| allocator.text(x.to_string())), + args.iter().map(|(x, t)| { + allocator.text(x.to_string()).append( + t.as_ref() + .map(|t| { + allocator + .text(":") + .append(allocator.space()) + .append(t.pretty(allocator)) + }) + .unwrap_or_else(|| allocator.nil()), + ) + }), allocator.text(","), ) .parens(), ) + .append( + rettype + .as_ref() + .map(|rettype| { + allocator + .space() + .append(allocator.text("->")) + .append(allocator.space()) + .append(rettype.pretty(allocator)) + }) + .unwrap_or_else(|| allocator.nil()), + ) .append(allocator.space()) .append(body.pretty(allocator)), + TopLevel::Structure(_, name, fields) => allocator + .text("struct") + .append(allocator.space()) + .append( + name.as_ref() + .map(|x| allocator.text(x.to_string())) + .unwrap_or_else(|| allocator.nil()), + ) + .append(allocator.space()) + .append(allocator.text("{")) + .append(allocator.hardline()) + .append( + allocator + .concat(fields.iter().map(|(name, ty)| { + allocator + .text(name.to_string()) + .append(allocator.text(":")) + .append(allocator.space()) + .append(ty.pretty(allocator)) + .append(allocator.text(";")) + .append(allocator.hardline()) + })) + .nest(2), + ) + .append(allocator.text("}")), } } } @@ -65,7 +113,28 @@ impl Expression { pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { match self { Expression::Value(_, val) => val.pretty(allocator), + Expression::Constructor(_, name, fields) => allocator + .text(name.to_string()) + .append(allocator.space()) + .append( + allocator + .concat(fields.iter().map(|(n, e)| { + allocator + .text(n.to_string()) + .append(allocator.text(":")) + .append(allocator.space()) + .append(e.pretty(allocator)) + .append(allocator.text(";")) + .append(allocator.hardline()) + })) + .nest(2) + .braces(), + ), Expression::Reference(_, var) => allocator.text(var.to_string()), + Expression::FieldRef(_, val, field) => val + .pretty(allocator) + .append(allocator.text(".")) + .append(allocator.text(field.to_string())), Expression::Cast(_, t, e) => allocator .text(t.clone()) .angles() @@ -151,6 +220,42 @@ fn type_suffix(x: &Option) -> &'static str { } } +impl Type { + pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { + match self { + Type::Named(x) => allocator.text(x.to_string()), + Type::Struct(name, fields) => allocator + .text("struct") + .append(allocator.space()) + .append( + name.as_ref() + .map(|x| allocator.text(x.to_string())) + .unwrap_or_else(|| allocator.nil()), + ) + .append(allocator.intersperse( + fields.iter().map(|(name, ty)| { + allocator + .text( + name.as_ref() + .map(|x| x.to_string()) + .unwrap_or_else(|| "_".to_string()), + ) + .append(allocator.text(":")) + .append(allocator.space()) + .append( + ty.as_ref() + .map(|x| x.pretty(allocator)) + .unwrap_or_else(|| allocator.text("_")), + ) + .append(allocator.text(";")) + }), + allocator.hardline(), + )) + .braces(), + } + } +} + derived_display!(Program); derived_display!(TopLevel); derived_display!(Statement); diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs index 0b0e486..fb139d9 100644 --- a/src/syntax/tokens.rs +++ b/src/syntax/tokens.rs @@ -36,12 +36,18 @@ pub enum Token { #[token("=")] Equals, + #[token(":")] + Colon, + #[token(";")] Semi, #[token(",")] Comma, + #[token(".")] + Dot, + #[token("(")] LeftParen, @@ -54,17 +60,26 @@ pub enum Token { #[token(">")] GreaterThan, + #[token("_")] + Underscore, + #[token("{")] OpenBrace, #[token("}")] CloseBrace, + #[token("->")] + SingleArrow, + #[token("λ")] #[token("lambda")] #[token("function")] Function, + #[token("struct")] + Struct, + // Next we take of any reserved words; I always like to put // these before we start recognizing more complicated regular // expressions. I don't think it matters, but it works for me. @@ -93,21 +108,31 @@ pub enum Token { // letter, too. #[regex(r"[a-z][a-zA-Z0-9_]*", |v| ArcIntern::new(v.slice().to_string()))] Variable(ArcIntern), + + // Type names; these are like variables, but must start with a capital + // letter. + #[regex(r"[A-Z][a-zA-Z0-9_]*", |v| ArcIntern::new(v.slice().to_string()))] + TypeName(ArcIntern), } impl fmt::Display for Token { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Token::Equals => write!(f, "'='"), + Token::Colon => write!(f, "':'"), Token::Semi => write!(f, "';'"), Token::Comma => write!(f, "','"), + Token::Dot => write!(f, "'.'"), Token::LeftParen => write!(f, "'('"), Token::RightParen => write!(f, "')'"), Token::LessThan => write!(f, "<"), Token::GreaterThan => write!(f, ">"), + Token::Underscore => write!(f, "_"), Token::OpenBrace => write!(f, "{{"), Token::CloseBrace => write!(f, "}}"), + Token::SingleArrow => write!(f, "->"), Token::Function => write!(f, "function"), + Token::Struct => write!(f, "struct"), Token::Print => write!(f, "'print'"), Token::Operator(c) => write!(f, "'{}'", c), Token::Number((None, otype, v)) => write!(f, "'{}{}'", v, display_optional_type(otype)), @@ -131,6 +156,7 @@ impl fmt::Display for Token { ) } Token::Variable(s) => write!(f, "'{}'", s), + Token::TypeName(s) => write!(f, "'{}'", s), } } } diff --git a/src/syntax/validate.rs b/src/syntax/validate.rs index 9013ac4..d751578 100644 --- a/src/syntax/validate.rs +++ b/src/syntax/validate.rs @@ -68,7 +68,7 @@ impl Program { /// actually a problem. pub fn validate(&self) -> (Vec, Vec) { let mut bound_variables = ScopedMap::new(); - println!("validate: {}", self); + println!("validate:\n{}", self); self.validate_with_bindings(&mut bound_variables) } @@ -85,7 +85,7 @@ impl Program { let mut warnings = vec![]; for stmt in self.items.iter() { - if let TopLevel::Function(Some(name), _, _) = stmt { + if let TopLevel::Function(Some(name), _, _, _) = stmt { bound_variables.insert(name.to_string(), name.location.clone()); } let (mut new_errors, mut new_warnings) = stmt.validate_with_bindings(bound_variables); @@ -120,12 +120,12 @@ impl TopLevel { bound_variables: &mut ScopedMap, ) -> (Vec, Vec) { match self { - TopLevel::Function(name, arguments, body) => { + TopLevel::Function(name, arguments, _, body) => { bound_variables.new_scope(); if let Some(name) = name { bound_variables.insert(name.name.clone(), name.location.clone()); } - for arg in arguments.iter() { + for (arg, _) in arguments.iter() { bound_variables.insert(arg.name.clone(), arg.location.clone()); } let result = body.validate(bound_variables); @@ -133,6 +133,7 @@ impl TopLevel { result } TopLevel::Statement(stmt) => stmt.validate(bound_variables), + TopLevel::Structure(_, _, _) => (vec![], vec![]), } } } @@ -198,11 +199,24 @@ impl Expression { ) -> (Vec, Vec) { match self { Expression::Value(_, _) => (vec![], vec![]), + Expression::Constructor(_, _, fields) => { + let mut errors = vec![]; + let mut warnings = vec![]; + + for (_, expr) in fields.iter() { + let (mut e, mut w) = expr.validate(variable_map); + errors.append(&mut e); + warnings.append(&mut w); + } + + (errors, warnings) + } Expression::Reference(_, var) if variable_map.contains_key(var) => (vec![], vec![]), Expression::Reference(loc, var) => ( vec![Error::UnboundVariable(loc.clone(), var.clone())], vec![], ), + Expression::FieldRef(_, exp, _) => exp.validate(variable_map), Expression::Cast(location, t, expr) => { let (mut errs, warns) = expr.validate(variable_map); diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 01b6955..5e004d8 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -44,7 +44,7 @@ pub fn convert_top_level( bindings: &mut HashMap, ir::TypeOrVar>, ) -> ir::TopLevel { match top_level { - syntax::TopLevel::Function(name, args, expr) => { + syntax::TopLevel::Function(name, args, _, expr) => { // First, at some point we're going to want to know a location for this function, // which should either be the name if we have one, or the body if we don't. let function_location = match name { @@ -76,7 +76,7 @@ pub fn convert_top_level( renames.new_scope(); let arginfo = args .iter() - .map(|name| { + .map(|(name, _)| { let new_type = ir::TypeOrVar::new(); constraint_db.push(Constraint::IsSomething( name.location.clone(), @@ -84,6 +84,7 @@ pub fn convert_top_level( )); let new_name = finalize_name(bindings, renames, name.clone()); bindings.insert(new_name.clone(), new_type.clone()); + unimplemented!(); (new_name, new_type) }) .collect::>(); @@ -119,6 +120,10 @@ pub fn convert_top_level( syntax::TopLevel::Statement(stmt) => { ir::TopLevel::Statement(convert_statement(stmt, constraint_db, renames, bindings)) } + + syntax::TopLevel::Structure(loc, oname, fields) => { + unimplemented!() + } } } @@ -253,6 +258,8 @@ fn convert_expression( } }, + syntax::Expression::Constructor(_, _, _) => unimplemented!(), + syntax::Expression::Reference(loc, name) => { let iname = ArcIntern::new(name); let final_name = renames.get(&iname).cloned().unwrap_or(iname); @@ -266,6 +273,8 @@ fn convert_expression( (refexp, rtype) } + syntax::Expression::FieldRef(_, _, _) => unimplemented!(), + syntax::Expression::Cast(loc, target, expr) => { let (nexpr, etype) = convert_expression(*expr, constraint_db, renames, bindings); let (prereqs, val_or_ref) = simplify_expr(nexpr); -- 2.53.0 From a7b85d37dae1c4744da077911d48ffec9e2a651f Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 16 Mar 2024 16:41:23 -0700 Subject: [PATCH 35/59] basic support for structures through the IR --- src/backend/into_crane.rs | 12 ++ src/ir/arbitrary.rs | 8 +- src/ir/ast.rs | 54 +++++++- src/ir/eval.rs | 25 ++++ src/ir/pretty.rs | 46 +++++++ src/syntax/ast.rs | 4 +- src/syntax/parser.lalrpop | 15 +- src/syntax/pretty.rs | 19 +-- src/type_infer.rs | 2 +- src/type_infer/convert.rs | 172 +++++++++++++++++++---- src/type_infer/finalize.rs | 36 ++++- src/type_infer/solve.rs | 273 ++++++++++++++++++++++++++++++++----- 12 files changed, 572 insertions(+), 94 deletions(-) diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index 3806c8a..fe7e1c0 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -33,6 +33,9 @@ impl Backend { types::Type::triple_pointer_type(&self.platform), ir::ArgumentExtension::None, ), + Type::Structure(_) => { + unimplemented!() + } Type::Primitive(PrimitiveType::Void) => (types::I8, ir::ArgumentExtension::None), // FIXME? Type::Primitive(PrimitiveType::I8) => (types::I8, ir::ArgumentExtension::Sext), Type::Primitive(PrimitiveType::I16) => (types::I16, ir::ArgumentExtension::Sext), @@ -88,6 +91,10 @@ impl Backend { self.defined_symbols .insert(top_level_name, (data_id, pt.into())); } + + Type::Structure(_) => { + unimplemented!() + } } } @@ -392,6 +399,9 @@ impl Backend { } } + Expression::Construct(_, _, _, _) => unimplemented!(), + Expression::FieldRef(_, _, _, _) => unimplemented!(), + Expression::Block(_, _, mut exprs) => match exprs.pop() { None => Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)), Some(last) => { @@ -511,8 +521,10 @@ impl Backend { Type::Function(_, _) => { panic!("function returns a function?") } + Type::Structure(_) => unimplemented!(), Type::Primitive(ct) => Ok((*result, ct.into())), }, + Type::Structure(_) => unimplemented!(), }, _ => panic!("don't support multi-value returns yet"), } diff --git a/src/ir/arbitrary.rs b/src/ir/arbitrary.rs index 71de979..b704eaa 100644 --- a/src/ir/arbitrary.rs +++ b/src/ir/arbitrary.rs @@ -9,6 +9,7 @@ use proptest::test_runner::{TestRng, TestRunner}; use rand::distributions::{Distribution, WeightedIndex}; use rand::seq::SliceRandom; use rand::Rng; +use std::collections::HashMap; use std::str::FromStr; lazy_static::lazy_static! { @@ -214,7 +215,10 @@ impl ProgramTree { } } - let current = Program { items }; + let current = Program { + items, + type_definitions: HashMap::new(), + }; ProgramTree { _rng: rng, current } } @@ -328,6 +332,7 @@ fn generate_random_expression( .expect("actually chose type"); Expression::Cast(Location::manufactured(), Type::Primitive(*to_type), inner) } + Type::Structure(_) => unimplemented!(), } } @@ -350,6 +355,7 @@ fn generate_random_expression( Expression::Primitive(Location::manufactured(), out_type, primop, args) } }, + Type::Structure(_) => unimplemented!(), } } diff --git a/src/ir/ast.rs b/src/ir/ast.rs index ae76a95..88820b6 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -2,6 +2,7 @@ use crate::eval::PrimitiveType; use crate::syntax::{ConstantType, Location}; use internment::ArcIntern; use proptest::arbitrary::Arbitrary; +use std::collections::HashMap; use std::convert::TryFrom; use std::str::FromStr; use std::sync::atomic::AtomicUsize; @@ -50,7 +51,9 @@ pub fn gensym(base: &str) -> Variable { pub struct Program { // For now, a program is just a vector of statements. In the future, we'll probably // extend this to include a bunch of other information, but for now: just a list. - pub(crate) items: Vec>, + pub items: Vec>, + // The set of types declared in this program. + pub type_definitions: HashMap, Type>, } impl Arbitrary for Program { @@ -103,6 +106,13 @@ pub enum Expression { Atomic(ValueOrRef), Cast(Location, Type, ValueOrRef), Primitive(Location, Type, Primitive, Vec>), + Construct( + Location, + Type, + ArcIntern, + HashMap, ValueOrRef>, + ), + FieldRef(Location, Type, ValueOrRef, ArcIntern), Block(Location, Type, Vec>), Print(Location, ValueOrRef), Call(Location, Type, Box>, Vec>), @@ -117,6 +127,8 @@ impl Expression { Expression::Atomic(x) => x.type_of(), Expression::Cast(_, t, _) => t.clone(), Expression::Primitive(_, t, _, _) => t.clone(), + Expression::Construct(_, t, _, _) => t.clone(), + Expression::FieldRef(_, t, _, _) => t.clone(), Expression::Block(_, t, _) => t.clone(), Expression::Print(_, _) => Type::void(), Expression::Call(_, t, _, _) => t.clone(), @@ -131,6 +143,8 @@ impl Expression { Expression::Atomic(ValueOrRef::Value(l, _, _)) => l, Expression::Cast(l, _, _) => l, Expression::Primitive(l, _, _, _) => l, + Expression::Construct(l, _, _, _) => l, + Expression::FieldRef(l, _, _, _) => l, Expression::Block(l, _, _) => l, Expression::Print(l, _) => l, Expression::Call(l, _, _, _) => l, @@ -233,6 +247,7 @@ impl Value { pub enum Type { Primitive(PrimitiveType), Function(Vec, Box), + Structure(HashMap, Type>), } impl Type { @@ -256,6 +271,7 @@ impl<'a> TryInto for &'a Type { match self { Type::Primitive(pt) => Ok((*pt).into()), Type::Function(_, _) => Err(()), + Type::Structure(_) => Err(()), } } } @@ -265,6 +281,7 @@ pub enum TypeOrVar { Primitive(PrimitiveType), Variable(Location, ArcIntern), Function(Vec, Box), + Structure(HashMap, TypeOrVar>), } impl Default for TypeOrVar { @@ -311,6 +328,10 @@ impl TypeOrVar { } TypeOrVar::Primitive(_) => false, + + TypeOrVar::Structure(fields) => { + fields.values_mut().any(|x| x.replace(name, replace_with)) + } } } @@ -323,6 +344,7 @@ impl TypeOrVar { TypeOrVar::Function(args, ret) => { args.iter().all(TypeOrVar::is_resolved) && ret.is_resolved() } + TypeOrVar::Structure(fields) => fields.values().all(TypeOrVar::is_resolved), } } } @@ -339,6 +361,16 @@ impl PartialEq for TypeOrVar { TypeOrVar::Primitive(x) => a == x, _ => false, }, + + Type::Structure(fields1) => match self { + TypeOrVar::Structure(fields2) => { + fields1.len() == fields2.len() + && fields1.iter().all(|(name, subtype)| { + fields2.get(name).map(|x| x == subtype).unwrap_or(false) + }) + } + _ => false, + }, } } } @@ -386,6 +418,9 @@ impl> From for TypeOrVar { args.into_iter().map(Into::into).collect(), Box::new((*ret).into()), ), + Type::Structure(fields) => { + TypeOrVar::Structure(fields.into_iter().map(|(n, t)| (n, t.into())).collect()) + } } } } @@ -413,7 +448,22 @@ impl TryFrom for Type { } TypeOrVar::Primitive(t) => Ok(Type::Primitive(t)), - _ => Err(value), + + TypeOrVar::Structure(fields) => { + let mut new_fields = HashMap::with_capacity(fields.len()); + + for (name, field) in fields.iter() { + if let Ok(new_field) = field.clone().try_into() { + new_fields.insert(name.clone(), new_field); + } else { + return Err(TypeOrVar::Structure(fields)); + } + } + + Ok(Type::Structure(new_fields)) + } + + TypeOrVar::Variable(_, _) => Err(value), } } } diff --git a/src/ir/eval.rs b/src/ir/eval.rs index 5b758ae..6c8b01c 100644 --- a/src/ir/eval.rs +++ b/src/ir/eval.rs @@ -2,6 +2,7 @@ use super::{Primitive, Type, ValueOrRef}; use crate::eval::{EvalError, Value}; use crate::ir::{Expression, Program, TopLevel, Variable}; use crate::util::scoped_map::ScopedMap; +use std::collections::HashMap; type IRValue = Value>; type IREvalError = EvalError>; @@ -60,6 +61,7 @@ where match ty { Type::Primitive(pt) => Ok(pt.safe_cast(&value)?), Type::Function(_, _) => Err(EvalError::CastToFunction(ty.to_string())), + Type::Structure(_) => unimplemented!(), } } @@ -79,6 +81,29 @@ where } } + Expression::Construct(_, _, name, fields) => { + let mut result_fields = HashMap::with_capacity(fields.len()); + + for (name, subexpr) in fields.iter() { + result_fields.insert(name.clone(), subexpr.eval(env)?); + } + + Ok(Value::Structure(Some(name.clone()), result_fields)) + } + + Expression::FieldRef(loc, _, valref, field) => match valref.eval(env)? { + Value::Structure(oname, mut fields) => match fields.remove(field) { + None => Err(EvalError::NoFieldForValue( + loc.clone(), + Value::Structure(oname, fields), + field.clone(), + )), + Some(value) => Ok(value), + }, + + x => Err(EvalError::NoFieldForValue(loc.clone(), x, field.clone())), + }, + Expression::Block(_, _, stmts) => { let mut result = Value::Void; diff --git a/src/ir/pretty.rs b/src/ir/pretty.rs index ae0ce15..b407059 100644 --- a/src/ir/pretty.rs +++ b/src/ir/pretty.rs @@ -56,6 +56,28 @@ impl Expression { Expression::Primitive(_, _, op, exprs) if exprs.len() == 1 => { op.pretty(allocator).append(exprs[0].pretty(allocator)) } + Expression::Construct(_, _, name, fields) => { + let inner = allocator + .intersperse( + fields.iter().map(|(k, v)| { + allocator + .text(k.to_string()) + .append(":") + .append(allocator.space()) + .append(v.pretty(allocator)) + .append(allocator.text(";")) + }), + allocator.line(), + ) + .indent(2) + .braces(); + allocator.text(name.to_string()).append(inner) + } + Expression::FieldRef(_, _, val, field) => val.pretty(allocator).append( + allocator + .text(".") + .append(allocator.text(field.to_string())), + ), Expression::Primitive(_, _, op, exprs) if exprs.len() == 2 => { let left = exprs[0].pretty(allocator); let right = exprs[1].pretty(allocator); @@ -180,6 +202,18 @@ impl Type { match self { Type::Function(args, rettype) => pretty_function_type!(allocator, args, rettype), Type::Primitive(prim) => prim.pretty(allocator), + Type::Structure(fields) => allocator.text("struct").append( + allocator + .concat(fields.iter().map(|(n, t)| { + allocator + .text(n.to_string()) + .append(allocator.text(":")) + .append(allocator.space()) + .append(t.pretty(allocator)) + .append(allocator.text(";")) + })) + .braces(), + ), } } } @@ -190,6 +224,18 @@ impl TypeOrVar { TypeOrVar::Function(args, rettype) => pretty_function_type!(allocator, args, rettype), TypeOrVar::Primitive(prim) => prim.pretty(allocator), TypeOrVar::Variable(_, name) => allocator.text(name.to_string()), + TypeOrVar::Structure(fields) => allocator.text("struct").append( + allocator + .concat(fields.iter().map(|(n, t)| { + allocator + .text(n.to_string()) + .append(allocator.text(":")) + .append(allocator.space()) + .append(t.pretty(allocator)) + .append(allocator.text(";")) + })) + .braces(), + ), } } } diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 92733df..2fff204 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -33,7 +33,7 @@ pub enum TopLevel { Option, Expression, ), - Structure(Location, Option, Vec<(Name, Type)>), + Structure(Location, Name, Vec<(Name, Type)>), } /// A Name. @@ -212,5 +212,5 @@ pub enum Value { #[derive(Clone, Debug, PartialEq, Eq)] pub enum Type { Named(Name), - Struct(Option, Vec<(Option, Option)>), + Struct(Vec<(Name, Option)>), } diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index e93e29f..70534ea 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -100,8 +100,8 @@ OptionalComma: () = { } Structure: TopLevel = { - "struct" "{" "}" => { - TopLevel::Structure(Location::new(file_idx, s..e), on, fields) + "struct" "{" "}" => { + TopLevel::Structure(Location::new(file_idx, s..e), n, fields) } } @@ -113,14 +113,13 @@ Field: (Name, Type) = { Type: Type = { => Type::Named(name), => Type::Named(t), - "struct" "{" "}" => - Type::Struct(on, fields), + "struct" "{" "}" => + Type::Struct(fields), } -TypeField: (Option, Option) = { - ":" ";" => (Some(name), Some(ty)), - (":" "_")? ";" => (Some(name), None), - "_" ":" ";" => (None, Some(ty)), +TypeField: (Name, Option) = { + ":" ";" => (name, Some(ty)), + (":" "_")? ";" => (name, None), } Name: Name = { diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index ea306b6..10fa97c 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -65,11 +65,7 @@ impl TopLevel { TopLevel::Structure(_, name, fields) => allocator .text("struct") .append(allocator.space()) - .append( - name.as_ref() - .map(|x| allocator.text(x.to_string())) - .unwrap_or_else(|| allocator.nil()), - ) + .append(allocator.text(name.to_string())) .append(allocator.space()) .append(allocator.text("{")) .append(allocator.hardline()) @@ -224,22 +220,13 @@ impl Type { pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { match self { Type::Named(x) => allocator.text(x.to_string()), - Type::Struct(name, fields) => allocator + Type::Struct(fields) => allocator .text("struct") .append(allocator.space()) - .append( - name.as_ref() - .map(|x| allocator.text(x.to_string())) - .unwrap_or_else(|| allocator.nil()), - ) .append(allocator.intersperse( fields.iter().map(|(name, ty)| { allocator - .text( - name.as_ref() - .map(|x| x.to_string()) - .unwrap_or_else(|| "_".to_string()), - ) + .text(name.to_string()) .append(allocator.text(":")) .append(allocator.space()) .append( diff --git a/src/type_infer.rs b/src/type_infer.rs index 080ce8d..31916dd 100644 --- a/src/type_infer.rs +++ b/src/type_infer.rs @@ -33,7 +33,7 @@ impl syntax::Program { /// this method, otherwise you may experience panics during operation. pub fn type_infer(self) -> TypeInferenceResult> { let (program, constraint_db) = convert_program(self); - let inference_result = solve_constraints(constraint_db); + let inference_result = solve_constraints(&program.type_definitions, constraint_db); inference_result.map(|resolutions| finalize_program(program, &resolutions)) } diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 5e004d8..1e103d3 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -7,6 +7,11 @@ use internment::ArcIntern; use std::collections::HashMap; use std::str::FromStr; +enum TopLevelItem { + Type(ArcIntern, ir::TypeOrVar), + Expression(ir::TopLevel), +} + /// This function takes a syntactic program and converts it into the IR version of the /// program, with appropriate type variables introduced and their constraints added to /// the given database. @@ -21,28 +26,37 @@ pub fn convert_program( let mut items = Vec::new(); let mut renames = ScopedMap::new(); let mut bindings = HashMap::new(); + let mut type_definitions = HashMap::new(); for item in program.items.drain(..) { - items.push(convert_top_level( - item, - &mut constraint_db, - &mut renames, - &mut bindings, - )); + let tli = convert_top_level(item, &mut constraint_db, &mut renames, &mut bindings); + + match tli { + TopLevelItem::Expression(item) => items.push(item), + TopLevelItem::Type(name, decl) => { + let _ = type_definitions.insert(name, decl); + } + } } - (ir::Program { items }, constraint_db) + ( + ir::Program { + items, + type_definitions, + }, + constraint_db, + ) } /// This function takes a top-level item and converts it into the IR version of the /// program, with all the appropriate type variables introduced and their constraints /// added to the given database. -pub fn convert_top_level( +fn convert_top_level( top_level: syntax::TopLevel, constraint_db: &mut Vec, renames: &mut ScopedMap, ArcIntern>, bindings: &mut HashMap, ir::TypeOrVar>, -) -> ir::TopLevel { +) -> TopLevelItem { match top_level { syntax::TopLevel::Function(name, args, _, expr) => { // First, at some point we're going to want to know a location for this function, @@ -75,8 +89,8 @@ pub fn convert_top_level( // appropriately. renames.new_scope(); let arginfo = args - .iter() - .map(|(name, _)| { + .into_iter() + .map(|(name, mut declared_type)| { let new_type = ir::TypeOrVar::new(); constraint_db.push(Constraint::IsSomething( name.location.clone(), @@ -84,7 +98,16 @@ pub fn convert_top_level( )); let new_name = finalize_name(bindings, renames, name.clone()); bindings.insert(new_name.clone(), new_type.clone()); - unimplemented!(); + + if let Some(declared_type) = declared_type.take() { + let declared_type = convert_type(declared_type, constraint_db); + constraint_db.push(Constraint::Equivalent( + name.location.clone(), + new_type.clone(), + declared_type, + )); + } + (new_name, new_type) }) .collect::>(); @@ -114,16 +137,27 @@ pub fn convert_top_level( // Remember to exit this scoping level! renames.release_scope(); - ir::TopLevel::Function(function_name, arginfo, rettype, expr) + TopLevelItem::Expression(ir::TopLevel::Function( + function_name, + arginfo, + rettype, + expr, + )) } - syntax::TopLevel::Statement(stmt) => { - ir::TopLevel::Statement(convert_statement(stmt, constraint_db, renames, bindings)) - } + syntax::TopLevel::Statement(stmt) => TopLevelItem::Expression(ir::TopLevel::Statement( + convert_statement(stmt, constraint_db, renames, bindings), + )), - syntax::TopLevel::Structure(loc, oname, fields) => { - unimplemented!() - } + syntax::TopLevel::Structure(_loc, name, fields) => TopLevelItem::Type( + name.intern(), + ir::TypeOrVar::Structure( + fields + .into_iter() + .map(|(name, t)| (name.intern(), convert_type(t, constraint_db))) + .collect(), + ), + ), } } @@ -258,7 +292,31 @@ fn convert_expression( } }, - syntax::Expression::Constructor(_, _, _) => unimplemented!(), + syntax::Expression::Constructor(loc, name, fields) => { + let mut result_fields = HashMap::new(); + let mut type_fields = HashMap::new(); + let mut prereqs = vec![]; + let result_type = ir::TypeOrVar::new(); + + for (name, syntax_expr) in fields.into_iter() { + let (ir_expr, expr_type) = + convert_expression(syntax_expr, constraint_db, renames, bindings); + type_fields.insert(name.clone().intern(), expr_type); + let (prereq, value) = simplify_expr(ir_expr); + result_fields.insert(name.clone().intern(), value); + merge_prereq(&mut prereqs, prereq); + } + + constraint_db.push(Constraint::NamedTypeIs( + loc.clone(), + name.clone().intern(), + ir::TypeOrVar::Structure(type_fields), + )); + let result = + ir::Expression::Construct(loc, result_type.clone(), name.intern(), result_fields); + + (finalize_expressions(prereqs, result), result_type) + } syntax::Expression::Reference(loc, name) => { let iname = ArcIntern::new(name); @@ -273,7 +331,26 @@ fn convert_expression( (refexp, rtype) } - syntax::Expression::FieldRef(_, _, _) => unimplemented!(), + syntax::Expression::FieldRef(loc, expr, field) => { + let (nexpr, etype) = convert_expression(*expr, constraint_db, renames, bindings); + let (prereqs, val_or_ref) = simplify_expr(nexpr); + let result_type = ir::TypeOrVar::new(); + let result = ir::Expression::FieldRef( + loc.clone(), + result_type.clone(), + val_or_ref, + field.clone().intern(), + ); + + constraint_db.push(Constraint::TypeHasField( + loc, + etype, + field.intern(), + result_type.clone(), + )); + + (finalize_expression(prereqs, result), result_type) + } syntax::Expression::Cast(loc, target, expr) => { let (nexpr, etype) = convert_expression(*expr, constraint_db, renames, bindings); @@ -366,15 +443,7 @@ fn convert_expression( let last_call = ir::Expression::Call(loc.clone(), return_type.clone(), Box::new(fun), new_args); - if prereqs.is_empty() { - (last_call, return_type) - } else { - prereqs.push(last_call); - ( - ir::Expression::Block(loc, return_type.clone(), prereqs), - return_type, - ) - } + (finalize_expressions(prereqs, last_call), return_type) } syntax::Expression::Block(loc, stmts) => { @@ -396,6 +465,35 @@ fn convert_expression( } } +fn convert_type(ty: syntax::Type, constraint_db: &mut Vec) -> ir::TypeOrVar { + match ty { + syntax::Type::Named(x) => match PrimitiveType::from_str(x.name.as_str()) { + Err(_) => { + let retval = ir::TypeOrVar::new_located(x.location.clone()); + constraint_db.push(Constraint::NamedTypeIs( + x.location.clone(), + x.intern(), + retval.clone(), + )); + retval + } + Ok(v) => ir::TypeOrVar::Primitive(v), + }, + syntax::Type::Struct(fields) => ir::TypeOrVar::Structure( + fields + .into_iter() + .map(|(n, t)| { + ( + n.intern(), + t.map(|x| convert_type(x, constraint_db)) + .unwrap_or_else(ir::TypeOrVar::new), + ) + }) + .collect(), + ), + } +} + fn simplify_expr( expr: ir::Expression, ) -> ( @@ -431,6 +529,20 @@ fn finalize_expression( } } +fn finalize_expressions( + mut prereqs: Vec>, + actual: ir::Expression, +) -> ir::Expression { + if prereqs.is_empty() { + actual + } else { + let return_type = actual.type_of(); + let loc = actual.location().clone(); + prereqs.push(actual); + ir::Expression::Block(loc, return_type, prereqs) + } +} + fn finalize_name( bindings: &HashMap, ir::TypeOrVar>, renames: &mut ScopedMap, ArcIntern>, diff --git a/src/type_infer/finalize.rs b/src/type_infer/finalize.rs index 883f2a2..1990332 100644 --- a/src/type_infer/finalize.rs +++ b/src/type_infer/finalize.rs @@ -3,7 +3,7 @@ use crate::eval::PrimitiveType; use crate::ir::{Expression, Program, TopLevel, Type, TypeOrVar, Value, ValueOrRef}; pub fn finalize_program( - mut program: Program, + program: Program, resolutions: &TypeResolutions, ) -> Program { for (name, ty) in resolutions.iter() { @@ -13,9 +13,15 @@ pub fn finalize_program( Program { items: program .items - .drain(..) + .into_iter() .map(|x| finalize_top_level(x, resolutions)) .collect(), + + type_definitions: program + .type_definitions + .into_iter() + .map(|(n, t)| (n, finalize_type(t, resolutions))) + .collect(), } } @@ -57,6 +63,23 @@ fn finalize_expression( .collect(), ), + Expression::Construct(loc, ty, name, fields) => Expression::Construct( + loc, + finalize_type(ty, resolutions), + name, + fields + .into_iter() + .map(|(k, v)| (k, finalize_val_or_ref(v, resolutions))) + .collect(), + ), + + Expression::FieldRef(loc, ty, valref, field) => Expression::FieldRef( + loc, + finalize_type(ty, resolutions), + finalize_val_or_ref(valref, resolutions), + field, + ), + Expression::Block(loc, ty, exprs) => { let mut final_exprs = Vec::with_capacity(exprs.len()); @@ -111,6 +134,12 @@ fn finalize_type(ty: TypeOrVar, resolutions: &TypeResolutions) -> Type { .collect(), Box::new(finalize_type(*ret, resolutions)), ), + TypeOrVar::Structure(fields) => Type::Structure( + fields + .into_iter() + .map(|(name, subtype)| (name, finalize_type(subtype, resolutions))) + .collect(), + ), } } @@ -129,6 +158,9 @@ fn finalize_val_or_ref( Type::Function(_, _) => { panic!("Somehow inferred that a constant was a function") } + Type::Structure(_) => { + panic!("Somehow inferred that a constant was a structure") + } Type::Primitive(PrimitiveType::Void) => { panic!("Somehow inferred that a constant was void") } diff --git a/src/type_infer/solve.rs b/src/type_infer/solve.rs index 82123b3..e2c715e 100644 --- a/src/type_infer/solve.rs +++ b/src/type_infer/solve.rs @@ -16,6 +16,9 @@ pub enum Constraint { ProperPrimitiveArgs(Location, Primitive, Vec, TypeOrVar), /// The given type can be casted to the target type safely CanCastTo(Location, TypeOrVar, TypeOrVar), + /// The given type has the given field in it, and the type of that field + /// is as given. + TypeHasField(Location, TypeOrVar, ArcIntern, TypeOrVar), /// The given type must be some numeric type, but this is not a constant /// value, so don't try to default it if we can't figure it out NumericType(Location, TypeOrVar), @@ -29,6 +32,8 @@ pub enum Constraint { IsSomething(Location, TypeOrVar), /// The given type can be negated IsSigned(Location, TypeOrVar), + /// Checks to see if the given named type is equivalent to the provided one. + NamedTypeIs(Location, ArcIntern, TypeOrVar), } impl fmt::Display for Constraint { @@ -44,11 +49,15 @@ impl fmt::Display for Constraint { } Constraint::ProperPrimitiveArgs(_, op, _, ret) => write!(f, "PRIM {} -> {}", op, ret), Constraint::CanCastTo(_, ty, ty2) => write!(f, "CAST {} -> {}", ty, ty2), + Constraint::TypeHasField(_, ty1, field, ty2) => { + write!(f, "FIELD {}.{} -> {}", ty1, field, ty2) + } Constraint::NumericType(_, ty) => write!(f, "NUMERIC {}", ty), Constraint::ConstantNumericType(_, ty) => write!(f, "CONST_NUMERIC {}", ty), Constraint::Equivalent(_, ty, ty2) => write!(f, "EQUIVALENT {} => {}", ty, ty2), Constraint::IsSomething(_, ty) => write!(f, "SOMETHING {}", ty), Constraint::IsSigned(_, ty) => write!(f, "SIGNED {}", ty), + Constraint::NamedTypeIs(_, name, ty) => write!(f, "TYPE_EQUIV {} == {}", name, ty), } } } @@ -65,6 +74,9 @@ impl Constraint { Constraint::CanCastTo(_, ty1, ty2) => { ty1.replace(name, replace_with) || ty2.replace(name, replace_with) } + Constraint::TypeHasField(_, ty1, _, ty2) => { + ty1.replace(name, replace_with) || ty2.replace(name, replace_with) + } Constraint::ConstantNumericType(_, ty) => ty.replace(name, replace_with), Constraint::Equivalent(_, ty1, ty2) => { ty1.replace(name, replace_with) || ty2.replace(name, replace_with) @@ -76,6 +88,7 @@ impl Constraint { ret.replace(name, replace_with) | args.iter_mut().any(|x| x.replace(name, replace_with)) } + Constraint::NamedTypeIs(_, name, ty) => ty.replace(name, replace_with), } } } @@ -142,21 +155,22 @@ pub enum TypeInferenceError { CannotSafelyCast(Location, PrimitiveType, PrimitiveType), /// The primitive invocation provided the wrong number of arguments. WrongPrimitiveArity(Location, Primitive, usize, usize, usize), - /// We cannot cast between the given function types, usually because they - /// have different argument lengths - CannotCastBetweenFunctinoTypes(Location, TypeOrVar, TypeOrVar), - /// We cannot cast from a function type to something else. - CannotCastFromFunctionType(Location, TypeOrVar), - /// We cannot cast to a function type from something else. - CannotCastToFunctionType(Location, TypeOrVar), + /// We cannot cast between the type types, for any number of reasons + CannotCast(Location, TypeOrVar, TypeOrVar), /// We cannot turn a number into a function. CannotMakeNumberAFunction(Location, TypeOrVar, Option), + /// We cannot turn a number into a Structure. + CannotMakeNumberAStructure(Location, TypeOrVar, Option), /// We had a constraint we just couldn't solve. CouldNotSolve(Constraint), /// Functions are not printable. FunctionsAreNotPrintable(Location), /// The given type isn't signed, and can't be negated - IsNotSigned(Location, PrimitiveType), + IsNotSigned(Location, TypeOrVar), + /// The given type doesn't have the given field. + NoFieldForType(Location, ArcIntern, TypeOrVar), + /// There is no type with the given name. + UnknownTypeName(Location, ArcIntern), } impl From for Diagnostic { @@ -196,22 +210,12 @@ impl From for Diagnostic { prim, observed )), - TypeInferenceError::CannotCastBetweenFunctinoTypes(loc, t1, t2) => loc - .labelled_error("cannot cast between function types") + TypeInferenceError::CannotCast(loc, t1, t2) => loc + .labelled_error("cannot cast between types") .with_message(format!( "tried to cast from {} to {}", t1, t2, )), - TypeInferenceError::CannotCastFromFunctionType(loc, t) => loc - .labelled_error("cannot cast from a function type to anything else") - .with_message(format!( - "function type was {}", t, - )), - TypeInferenceError::CannotCastToFunctionType(loc, t) => loc - .labelled_error("cannot cast to a function type") - .with_message(format!( - "function type was {}", t, - )), TypeInferenceError::CannotMakeNumberAFunction(loc, t, val) => loc .labelled_error(if let Some(val) = val { format!("cannot turn {} into a function", val) @@ -219,17 +223,32 @@ impl From for Diagnostic { "cannot use a constant as a function type".to_string() }) .with_message(format!("function type was {}", t)), + TypeInferenceError::CannotMakeNumberAStructure(loc, t, val) => loc + .labelled_error(if let Some(val) = val { + format!("cannot turn {} into a function", val) + } else { + "cannot use a constant as a function type".to_string() + }) + .with_message(format!("function type was {}", t)), TypeInferenceError::FunctionsAreNotPrintable(loc) => loc .labelled_error("cannot print function values"), TypeInferenceError::IsNotSigned(loc, pt) => loc .labelled_error(format!("type {} is not signed", pt)) .with_message("and so it cannot be negated"), + TypeInferenceError::NoFieldForType(loc, field, t) => loc + .labelled_error(format!("no field {} available for type {}", field, t)), + TypeInferenceError::UnknownTypeName(loc , name) => loc + .labelled_error(format!("unknown type named {}", name)), TypeInferenceError::CouldNotSolve(Constraint::CanCastTo(loc, a, b)) => { loc.labelled_error("internal error").with_message(format!( - "could not determine if it was safe to cast from {} to {:#?}", + "could not determine if it was safe to cast from {} to {}", a, b )) } + TypeInferenceError::CouldNotSolve(Constraint::TypeHasField(loc, a, field, _)) => { + loc.labelled_error("internal error") + .with_message(format!("fould not determine if type {} has field {}", a, field)) + } TypeInferenceError::CouldNotSolve(Constraint::Equivalent(loc, a, b)) => { loc.labelled_error("internal error").with_message(format!( "could not determine if {} and {} were equivalent", @@ -263,6 +282,9 @@ impl From for Diagnostic { TypeInferenceError::CouldNotSolve(Constraint::IsSigned(loc, t)) => loc .labelled_error("internal error") .with_message(format!("could not infer that type {} was signed", t)), + TypeInferenceError::CouldNotSolve(Constraint::NamedTypeIs(loc, name, ty)) => loc + .labelled_error("internal error") + .with_message(format!("could not infer that the name {} refers to {}", name, ty)), } } } @@ -296,6 +318,7 @@ impl From for Diagnostic { /// successful set of type resolutions (mappings from type variables to their values), or /// a series of inference errors. pub fn solve_constraints( + known_types: &HashMap, TypeOrVar>, mut constraint_db: Vec, ) -> TypeInferenceResult { let mut errors = vec![]; @@ -346,7 +369,7 @@ pub fn solve_constraints( } all_constraints_solved = false; } else { - errors.push(TypeInferenceError::CannotCastBetweenFunctinoTypes( + errors.push(TypeInferenceError::CannotCast( loc, TypeOrVar::Function(args1, ret1), TypeOrVar::Function(args2, ret2), @@ -360,21 +383,100 @@ pub fn solve_constraints( Constraint::CanCastTo( loc, - ft @ TypeOrVar::Function(_, _), - pt @ TypeOrVar::Primitive(_), + st1 @ TypeOrVar::Structure(_), + st2 @ TypeOrVar::Structure(_), ) => { - tracing::trace!(function_type = %ft, primitive_type = %pt, "we can't cast a function type to a primitive type"); - errors.push(TypeInferenceError::CannotCastFromFunctionType(loc, pt)); + tracing::trace!( + "structures can be equivalent, if their fields and types are exactly the same" + ); + new_constraints.push(Constraint::Equivalent(loc, st1, st2)); + changed_something = true; + } + + Constraint::CanCastTo( + loc, + ft @ TypeOrVar::Function(_, _), + ot @ TypeOrVar::Primitive(_) | ot @ TypeOrVar::Structure(_), + ) => { + tracing::trace!(function_type = %ft, other_type = %ot, "we can't cast a function type to a primitive or structure type"); + errors.push(TypeInferenceError::CannotCast(loc, ft, ot)); changed_something = true; } Constraint::CanCastTo( loc, pt @ TypeOrVar::Primitive(_), - ft @ TypeOrVar::Function(_, _), + ot @ TypeOrVar::Function(_, _) | ot @ TypeOrVar::Structure(_), ) => { - tracing::trace!(function_type = %ft, primitive_type = %pt, "we can't cast a primitive type to a function type"); - errors.push(TypeInferenceError::CannotCastToFunctionType(loc, pt)); + tracing::trace!(other_type = %ot, primitive_type = %pt, "we can't cast a primitive type to a function or structure type"); + errors.push(TypeInferenceError::CannotCast(loc, pt, ot)); + changed_something = true; + } + + Constraint::CanCastTo( + loc, + st @ TypeOrVar::Structure(_), + ot @ TypeOrVar::Primitive(_) | ot @ TypeOrVar::Function(_, _), + ) => { + tracing::trace!(structure_type = %st, other_type = %ot, "we can't cast a structure type to a function or primitive type"); + errors.push(TypeInferenceError::CannotCast(loc, st, ot)); + changed_something = true; + } + + Constraint::NamedTypeIs(loc, name, ty) => match known_types.get(&name) { + None => { + tracing::trace!(type_name = %name, "we don't know a type named name"); + errors.push(TypeInferenceError::UnknownTypeName(loc, name)); + changed_something = true; + } + + Some(declared_type) => { + tracing::trace!(type_name = %name, declared = %declared_type, provided = %ty, "validating that named type is equivalent to provided"); + new_constraints.push(Constraint::Equivalent( + loc, + declared_type.clone(), + ty, + )); + changed_something = true; + } + }, + + Constraint::TypeHasField( + loc, + TypeOrVar::Structure(mut fields), + field, + result_type, + ) => match fields.remove(&field) { + None => { + let reconstituted = TypeOrVar::Structure(fields); + tracing::trace!(structure_type = %reconstituted, %field, "no field found in type"); + errors.push(TypeInferenceError::NoFieldForType( + loc, + field, + reconstituted, + )); + changed_something = true; + } + + Some(field_subtype) => { + tracing::trace!(%field_subtype, %result_type, %field, "validating that field's subtype matches target result type"); + new_constraints.push(Constraint::Equivalent( + loc, + result_type, + field_subtype, + )); + changed_something = true; + } + }, + + Constraint::TypeHasField( + loc, + ot @ TypeOrVar::Primitive(_) | ot @ TypeOrVar::Function(_, _), + field, + _, + ) => { + tracing::trace!(other_type = %ot, %field, "can't get field from primitive or function type"); + errors.push(TypeInferenceError::NoFieldForType(loc, field, ot)); changed_something = true; } @@ -394,6 +496,13 @@ pub fn solve_constraints( changed_something = true; } + // if we're testing if a function type is numeric, then throw a useful warning + Constraint::ConstantNumericType(loc, t @ TypeOrVar::Structure(_)) => { + tracing::trace!(structure_type = %t, "structures can't be constant numbers"); + errors.push(TypeInferenceError::CannotMakeNumberAStructure(loc, t, None)); + changed_something = true; + } + // if we're testing if a number can fit into a numeric type, we can just do that! Constraint::FitsInNumType(loc, TypeOrVar::Primitive(ctype), val) => { match ctype.max_value() { @@ -420,9 +529,21 @@ pub fn solve_constraints( changed_something = true; } + // if we're testing if a function type can fit into a numeric type, that's a problem + Constraint::FitsInNumType(loc, t @ TypeOrVar::Structure(_), val) => { + tracing::trace!(function_type = %t, "values don't fit in structure types"); + errors.push(TypeInferenceError::CannotMakeNumberAStructure( + loc, + t, + Some(val), + )); + changed_something = true; + } + // if we want to know if a type is something, and it is something, then we're done Constraint::IsSomething(_, t @ TypeOrVar::Function(_, _)) - | Constraint::IsSomething(_, t @ TypeOrVar::Primitive(_)) => { + | Constraint::IsSomething(_, t @ TypeOrVar::Primitive(_)) + | Constraint::IsSomething(_, t @ TypeOrVar::Structure(_)) => { tracing::trace!(tested_type = %t, "type is definitely something"); changed_something = true; } @@ -431,7 +552,10 @@ pub fn solve_constraints( Constraint::IsSigned(loc, TypeOrVar::Primitive(pt)) => { tracing::trace!(primitive_type = %pt, "we can check if a primitive is signed"); if !pt.valid_operators().contains(&("-", 1)) { - errors.push(TypeInferenceError::IsNotSigned(loc, pt)); + errors.push(TypeInferenceError::IsNotSigned( + loc, + TypeOrVar::Primitive(pt), + )); } changed_something = true; } @@ -439,7 +563,14 @@ pub fn solve_constraints( // again with the functions and the numbers Constraint::IsSigned(loc, t @ TypeOrVar::Function(_, _)) => { tracing::trace!(function_type = %t, "functions are not signed"); - errors.push(TypeInferenceError::CannotCastFromFunctionType(loc, t)); + errors.push(TypeInferenceError::IsNotSigned(loc, t)); + changed_something = true; + } + + // again with the functions and the numbers + Constraint::IsSigned(loc, t @ TypeOrVar::Structure(_)) => { + tracing::trace!(structure_type = %t, "structures are not signed"); + errors.push(TypeInferenceError::IsNotSigned(loc, t)); changed_something = true; } @@ -459,6 +590,13 @@ pub fn solve_constraints( changed_something = true; } + // if we're testing if a structure type is numeric, then throw a useful warning + Constraint::NumericType(loc, t @ TypeOrVar::Structure(_)) => { + tracing::trace!(structure_type = %t, "structure types aren't numeric"); + errors.push(TypeInferenceError::CannotMakeNumberAStructure(loc, t, None)); + changed_something = true; + } + // all of our primitive types are printable Constraint::Printable(_, TypeOrVar::Primitive(pt)) => { tracing::trace!(primitive_type = %pt, "primitive types are printable"); @@ -472,6 +610,17 @@ pub fn solve_constraints( changed_something = true; } + // structure types are printable if all the types inside them are printable + Constraint::Printable(loc, TypeOrVar::Structure(fields)) => { + tracing::trace!( + "structure types are printable if all their subtypes are printable" + ); + for (_, subtype) in fields.into_iter() { + new_constraints.push(Constraint::Printable(loc.clone(), subtype)); + } + changed_something = true; + } + Constraint::ProperPrimitiveArgs(loc, prim, mut args, ret) => match prim { Primitive::Plus | Primitive::Minus | Primitive::Times | Primitive::Divide if args.len() == 2 => @@ -558,6 +707,36 @@ pub fn solve_constraints( changed_something = true; } + Constraint::Equivalent( + loc, + pt @ TypeOrVar::Primitive(_), + st @ TypeOrVar::Structure(_), + ) + | Constraint::Equivalent( + loc, + st @ TypeOrVar::Structure(_), + pt @ TypeOrVar::Primitive(_), + ) => { + tracing::trace!(primitive_type = %pt, structure_type = %st, "structure and primitive types cannot be equivalent"); + errors.push(TypeInferenceError::NotEquivalent(loc, pt, st)); + changed_something = true; + } + + Constraint::Equivalent( + loc, + st @ TypeOrVar::Structure(_), + ft @ TypeOrVar::Function(_, _), + ) + | Constraint::Equivalent( + loc, + ft @ TypeOrVar::Function(_, _), + st @ TypeOrVar::Structure(_), + ) => { + tracing::trace!(structure_type = %st, function_type = %ft, "structure and primitive types cannot be equivalent"); + errors.push(TypeInferenceError::NotEquivalent(loc, st, ft)); + changed_something = true; + } + Constraint::Equivalent( _, TypeOrVar::Variable(_, name1), @@ -588,6 +767,35 @@ pub fn solve_constraints( tracing::trace!("we checked/rewrote if function types are equivalent"); } + Constraint::Equivalent( + loc, + TypeOrVar::Structure(fields1), + TypeOrVar::Structure(mut fields2), + ) => { + if fields1.len() == fields2.len() + && fields1.keys().all(|x| fields2.contains_key(x)) + { + for (name, subtype1) in fields1.into_iter() { + let subtype2 = fields2 + .remove(&name) + .expect("can find matching field after equivalence check"); + new_constraints.push(Constraint::Equivalent( + loc.clone(), + subtype1, + subtype2, + )); + } + } else { + errors.push(TypeInferenceError::NotEquivalent( + loc, + TypeOrVar::Structure(fields1), + TypeOrVar::Structure(fields2), + )) + } + changed_something = true; + tracing::trace!("we checked/rewrote if structures are equivalent"); + } + Constraint::Equivalent(_, TypeOrVar::Variable(_, ref name), ref rhs) => { changed_something |= replace_variable(&mut constraint_db, name, rhs); changed_something |= replace_variable(&mut new_constraints, name, rhs); @@ -607,6 +815,7 @@ pub fn solve_constraints( Constraint::CanCastTo(_, TypeOrVar::Variable(_, _), _) | Constraint::CanCastTo(_, _, TypeOrVar::Variable(_, _)) + | Constraint::TypeHasField(_, TypeOrVar::Variable(_, _), _, _) | Constraint::ConstantNumericType(_, TypeOrVar::Variable(_, _)) | Constraint::FitsInNumType(_, TypeOrVar::Variable(_, _), _) | Constraint::IsSomething(_, TypeOrVar::Variable(_, _)) -- 2.53.0 From 854fd601324744af4c65ef97994f02f415737de0 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Fri, 29 Mar 2024 10:45:55 -0700 Subject: [PATCH 36/59] Add a Fields structure. --- src/ir.rs | 1 + src/ir/ast.rs | 24 +++++---- src/ir/fields.rs | 101 +++++++++++++++++++++++++++++++++++++ src/type_infer/convert.rs | 44 ++++++++-------- src/type_infer/finalize.rs | 9 ++-- src/type_infer/solve.rs | 8 +-- 6 files changed, 145 insertions(+), 42 deletions(-) create mode 100644 src/ir/fields.rs diff --git a/src/ir.rs b/src/ir.rs index 48ac76f..5308405 100644 --- a/src/ir.rs +++ b/src/ir.rs @@ -15,6 +15,7 @@ mod arbitrary; pub mod ast; mod eval; +mod fields; mod pretty; mod strings; mod top_level; diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 88820b6..8d86fdf 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -1,4 +1,5 @@ use crate::eval::PrimitiveType; +pub use crate::ir::fields::Fields; use crate::syntax::{ConstantType, Location}; use internment::ArcIntern; use proptest::arbitrary::Arbitrary; @@ -247,7 +248,7 @@ impl Value { pub enum Type { Primitive(PrimitiveType), Function(Vec, Box), - Structure(HashMap, Type>), + Structure(Fields), } impl Type { @@ -281,7 +282,7 @@ pub enum TypeOrVar { Primitive(PrimitiveType), Variable(Location, ArcIntern), Function(Vec, Box), - Structure(HashMap, TypeOrVar>), + Structure(Fields), } impl Default for TypeOrVar { @@ -330,7 +331,7 @@ impl TypeOrVar { TypeOrVar::Primitive(_) => false, TypeOrVar::Structure(fields) => { - fields.values_mut().any(|x| x.replace(name, replace_with)) + fields.types_mut().any(|x| x.replace(name, replace_with)) } } } @@ -344,7 +345,7 @@ impl TypeOrVar { TypeOrVar::Function(args, ret) => { args.iter().all(TypeOrVar::is_resolved) && ret.is_resolved() } - TypeOrVar::Structure(fields) => fields.values().all(TypeOrVar::is_resolved), + TypeOrVar::Structure(fields) => fields.types().all(TypeOrVar::is_resolved), } } } @@ -364,7 +365,7 @@ impl PartialEq for TypeOrVar { Type::Structure(fields1) => match self { TypeOrVar::Structure(fields2) => { - fields1.len() == fields2.len() + fields1.count() == fields2.count() && fields1.iter().all(|(name, subtype)| { fields2.get(name).map(|x| x == subtype).unwrap_or(false) }) @@ -418,9 +419,7 @@ impl> From for TypeOrVar { args.into_iter().map(Into::into).collect(), Box::new((*ret).into()), ), - Type::Structure(fields) => { - TypeOrVar::Structure(fields.into_iter().map(|(n, t)| (n, t.into())).collect()) - } + Type::Structure(fields) => TypeOrVar::Structure(fields.map(Into::into)), } } } @@ -450,16 +449,21 @@ impl TryFrom for Type { TypeOrVar::Primitive(t) => Ok(Type::Primitive(t)), TypeOrVar::Structure(fields) => { - let mut new_fields = HashMap::with_capacity(fields.len()); + let mut new_fields = Fields::new(fields.ordering()); + let mut errored = false; for (name, field) in fields.iter() { if let Ok(new_field) = field.clone().try_into() { new_fields.insert(name.clone(), new_field); } else { - return Err(TypeOrVar::Structure(fields)); + errored = true; } } + if errored { + return Err(TypeOrVar::Structure(fields)); + } + Ok(Type::Structure(new_fields)) } diff --git a/src/ir/fields.rs b/src/ir/fields.rs new file mode 100644 index 0000000..71a541c --- /dev/null +++ b/src/ir/fields.rs @@ -0,0 +1,101 @@ +use internment::ArcIntern; +use std::fmt; + +#[derive(Clone, PartialEq, Eq)] +pub struct Fields { + ordering: FieldOrdering, + fields: Vec<(ArcIntern, T)>, +} + +impl fmt::Debug for Fields { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Fields:")?; + self.fields.fmt(f) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum FieldOrdering { + Standard, +} + +impl Default for Fields { + fn default() -> Self { + Self::new(FieldOrdering::Standard) + } +} + +impl Fields { + pub fn new(ordering: FieldOrdering) -> Fields { + Fields { + ordering, + fields: vec![], + } + } + + pub fn ordering(&self) -> FieldOrdering { + self.ordering + } + + pub fn insert(&mut self, name: ArcIntern, t: T) { + self.fields.push((name, t)); + } + + pub fn get(&self, name: &ArcIntern) -> Option<&T> { + for (n, res) in self.fields.iter() { + if n == name { + return Some(res); + } + } + + None + } + + pub fn map T2>(self, f: F) -> Fields { + Fields { + ordering: self.ordering, + fields: self.fields.into_iter().map(|(n, t)| (n, f(t))).collect(), + } + } + + pub fn count(&self) -> usize { + self.fields.len() + } + + pub fn has_field(&self, name: &ArcIntern) -> bool { + self.fields.iter().any(|(current, _)| current == name) + } + + pub fn remove_field(&mut self, name: &ArcIntern) -> Option { + let mut field_index = None; + + for (idx, (current, _)) in self.fields.iter().enumerate() { + if current == name { + field_index = Some(idx); + break; + } + } + + field_index.map(|i| self.fields.remove(i).1) + } + + pub fn iter(&self) -> impl Iterator, &T)> { + self.fields.iter().map(|(x, y)| (x, y)) + } + + pub fn into_iter(self) -> impl Iterator, T)> { + self.fields.into_iter() + } + + pub fn field_names(&self) -> impl Iterator> { + self.fields.iter().map(|(n, _)| n) + } + + pub fn types(&self) -> impl Iterator { + self.fields.iter().map(|(_, x)| x) + } + + pub fn types_mut(&mut self) -> impl Iterator { + self.fields.iter_mut().map(|(_, x)| x) + } +} diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 1e103d3..c6c9a88 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -149,15 +149,15 @@ fn convert_top_level( convert_statement(stmt, constraint_db, renames, bindings), )), - syntax::TopLevel::Structure(_loc, name, fields) => TopLevelItem::Type( - name.intern(), - ir::TypeOrVar::Structure( - fields - .into_iter() - .map(|(name, t)| (name.intern(), convert_type(t, constraint_db))) - .collect(), - ), - ), + syntax::TopLevel::Structure(_loc, name, fields) => { + let mut updated_fields = ir::Fields::default(); + + for (name, field_type) in fields.into_iter() { + updated_fields.insert(name.intern(), convert_type(field_type, constraint_db)); + } + + TopLevelItem::Type(name.intern(), ir::TypeOrVar::Structure(updated_fields)) + } } } @@ -294,7 +294,7 @@ fn convert_expression( syntax::Expression::Constructor(loc, name, fields) => { let mut result_fields = HashMap::new(); - let mut type_fields = HashMap::new(); + let mut type_fields = ir::Fields::default(); let mut prereqs = vec![]; let result_type = ir::TypeOrVar::new(); @@ -479,18 +479,18 @@ fn convert_type(ty: syntax::Type, constraint_db: &mut Vec) -> ir::Ty } Ok(v) => ir::TypeOrVar::Primitive(v), }, - syntax::Type::Struct(fields) => ir::TypeOrVar::Structure( - fields - .into_iter() - .map(|(n, t)| { - ( - n.intern(), - t.map(|x| convert_type(x, constraint_db)) - .unwrap_or_else(ir::TypeOrVar::new), - ) - }) - .collect(), - ), + syntax::Type::Struct(fields) => { + let mut new_fields = ir::Fields::default(); + + for (name, field_type) in fields.into_iter() { + let new_field_type = field_type + .map(|x| convert_type(x, constraint_db)) + .unwrap_or_else(ir::TypeOrVar::new); + new_fields.insert(name.intern(), new_field_type); + } + + ir::TypeOrVar::Structure(new_fields) + } } } diff --git a/src/type_infer/finalize.rs b/src/type_infer/finalize.rs index 1990332..03edec2 100644 --- a/src/type_infer/finalize.rs +++ b/src/type_infer/finalize.rs @@ -134,12 +134,9 @@ fn finalize_type(ty: TypeOrVar, resolutions: &TypeResolutions) -> Type { .collect(), Box::new(finalize_type(*ret, resolutions)), ), - TypeOrVar::Structure(fields) => Type::Structure( - fields - .into_iter() - .map(|(name, subtype)| (name, finalize_type(subtype, resolutions))) - .collect(), - ), + TypeOrVar::Structure(fields) => { + Type::Structure(fields.map(|subtype| finalize_type(subtype, resolutions))) + } } } diff --git a/src/type_infer/solve.rs b/src/type_infer/solve.rs index e2c715e..e0b69a7 100644 --- a/src/type_infer/solve.rs +++ b/src/type_infer/solve.rs @@ -446,7 +446,7 @@ pub fn solve_constraints( TypeOrVar::Structure(mut fields), field, result_type, - ) => match fields.remove(&field) { + ) => match fields.remove_field(&field) { None => { let reconstituted = TypeOrVar::Structure(fields); tracing::trace!(structure_type = %reconstituted, %field, "no field found in type"); @@ -772,12 +772,12 @@ pub fn solve_constraints( TypeOrVar::Structure(fields1), TypeOrVar::Structure(mut fields2), ) => { - if fields1.len() == fields2.len() - && fields1.keys().all(|x| fields2.contains_key(x)) + if fields1.count() == fields2.count() + && fields1.field_names().all(|x| fields2.has_field(x)) { for (name, subtype1) in fields1.into_iter() { let subtype2 = fields2 - .remove(&name) + .remove_field(&name) .expect("can find matching field after equivalence check"); new_constraints.push(Constraint::Equivalent( loc.clone(), -- 2.53.0 From e1e798ef8e53152d35620e6c00623e2cfa2d3b43 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 30 Mar 2024 21:17:11 -0700 Subject: [PATCH 37/59] Get to the point of needing to construct/reference fields. --- src/backend.rs | 5 +- src/backend/error.rs | 7 +- src/backend/into_crane.rs | 258 ++++++++++++++++++-------------------- src/ir/ast.rs | 10 ++ src/ir/fields.rs | 40 +++++- src/syntax/tokens.rs | 8 -- 6 files changed, 176 insertions(+), 152 deletions(-) diff --git a/src/backend.rs b/src/backend.rs index b2fee29..6047134 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -35,6 +35,7 @@ pub use self::error::BackendError; pub use self::runtime::{RuntimeFunctionError, RuntimeFunctions}; use crate::syntax::ConstantType; use cranelift_codegen::entity::EntityRef; +use cranelift_codegen::ir::types; use cranelift_codegen::settings::Configurable; use cranelift_codegen::{isa, settings}; use cranelift_jit::{JITBuilder, JITModule}; @@ -61,7 +62,7 @@ pub struct Backend { runtime_functions: RuntimeFunctions, defined_strings: HashMap, defined_functions: HashMap, FuncId>, - defined_symbols: HashMap, (DataId, ConstantType)>, + defined_symbols: HashMap, (DataId, types::Type)>, output_buffer: Option, platform: Triple, next_variable: usize, @@ -190,7 +191,7 @@ impl Backend { self.module.define_data(id, &self.data_ctx)?; self.data_ctx.clear(); self.defined_symbols - .insert(ArcIntern::new(name), (id, ctype)); + .insert(ArcIntern::new(name), (id, ctype.into())); Ok(id) } diff --git a/src/backend/error.rs b/src/backend/error.rs index c6dc995..0fd0e7e 100644 --- a/src/backend/error.rs +++ b/src/backend/error.rs @@ -1,4 +1,4 @@ -use crate::{backend::runtime::RuntimeFunctionError, eval::PrimitiveType, ir::Type}; +use crate::{backend::runtime::RuntimeFunctionError, ir::Type}; use codespan_reporting::diagnostic::Diagnostic; use cranelift_codegen::{isa::LookupError, settings::SetError, CodegenError}; use cranelift_module::ModuleError; @@ -40,7 +40,10 @@ pub enum BackendError { #[error(transparent)] Write(#[from] cranelift_object::object::write::Error), #[error("Invalid type cast from {from} to {to}")] - InvalidTypeCast { from: PrimitiveType, to: Type }, + InvalidTypeCast { + from: cranelift_codegen::ir::types::Type, + to: Type, + }, #[error("Unknown string constant '{0}")] UnknownString(ArcIntern), #[error("Compiler doesn't currently support function arguments")] diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index fe7e1c0..644af63 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -14,14 +14,16 @@ use cranelift_module::{DataDescription, FuncId, Linkage, Module}; use internment::ArcIntern; use std::collections::{hash_map, HashMap}; +const VOID_REPR_TYPE: types::Type = types::I64; + /// When we're talking about variables, it's handy to just have a table that points /// from a variable to "what to do if you want to reference this variable", which is /// agnostic about whether the variable is local, global, an argument, etc. Since /// the type of that function is a little bit annoying, we summarize it here. pub enum ReferenceBuilder { - Global(ConstantType, GlobalValue), - Local(ConstantType, cranelift_frontend::Variable), - Argument(ConstantType, entities::Value), + Global(types::Type, GlobalValue), + Local(types::Type, cranelift_frontend::Variable), + Argument(types::Type, entities::Value), } impl Backend { @@ -29,13 +31,10 @@ impl Backend { /// best as possible. fn translate_type(&self, t: &Type) -> AbiParam { let (value_type, extension) = match t { - Type::Function(_, _) => ( + Type::Function(_, _) | Type::Structure(_) => ( types::Type::triple_pointer_type(&self.platform), ir::ArgumentExtension::None, ), - Type::Structure(_) => { - unimplemented!() - } Type::Primitive(PrimitiveType::Void) => (types::I8, ir::ArgumentExtension::None), // FIXME? Type::Primitive(PrimitiveType::I8) => (types::I8, ir::ArgumentExtension::Sext), Type::Primitive(PrimitiveType::I16) => (types::I16, ir::ArgumentExtension::Sext), @@ -88,12 +87,23 @@ impl Backend { )?; tracing::info!(name = %top_level_name, data_type = %pt, "defining top-level data"); self.module.define_data(data_id, &pt.blank_data())?; + let constant_type = ConstantType::from(pt); self.defined_symbols - .insert(top_level_name, (data_id, pt.into())); + .insert(top_level_name, (data_id, constant_type.into())); } - Type::Structure(_) => { - unimplemented!() + Type::Structure(mut fields) => { + let data_id = self.module.declare_data( + top_level_name.as_str(), + Linkage::Export, + true, + false, + )?; + tracing::info!(name = %top_level_name, "defining top-level data structure"); + self.module.define_data(data_id, fields.blank_data())?; + let pointer = self.module.target_config().pointer_type(); + self.defined_symbols + .insert(top_level_name, (data_id, pointer)); } } } @@ -226,13 +236,8 @@ impl Backend { let main_block = builder.create_block(); // add the block parameters, which should be the function parameters for (name, ty) in arguments.iter() { - let constant_type = ty - .try_into() - .map_err(|_| BackendError::NoFunctionArguments { - function_name: function_name.to_string(), - argument_name: name.to_string(), - })?; - let value = builder.append_block_param(main_block, ir::Type::from(constant_type)); + let constant_type = self.translate_type(ty).value_type; + let value = builder.append_block_param(main_block, constant_type); variables.insert( name.clone(), ReferenceBuilder::Argument(constant_type, value), @@ -273,96 +278,98 @@ impl Backend { expr: Expression, variables: &mut HashMap, builder: &mut FunctionBuilder, - ) -> Result<(entities::Value, ConstantType), BackendError> { + ) -> Result<(entities::Value, types::Type), BackendError> { match expr { Expression::Atomic(x) => self.compile_value_or_ref(x, variables, builder), Expression::Cast(_, target_type, valref) => { + let val_is_signed = valref.type_of().is_signed(); let (val, val_type) = self.compile_value_or_ref(valref, variables, builder)?; match (val_type, &target_type) { - (ConstantType::I8, Type::Primitive(PrimitiveType::I8)) => Ok((val, val_type)), - (ConstantType::I8, Type::Primitive(PrimitiveType::I16)) => { - Ok((builder.ins().sextend(types::I16, val), ConstantType::I16)) + (types::I8, Type::Primitive(PrimitiveType::I8)) => Ok((val, val_type)), + (types::I8, Type::Primitive(PrimitiveType::I16)) => { + if val_is_signed { + Ok((builder.ins().sextend(types::I16, val), types::I16)) + } else { + Ok((builder.ins().uextend(types::I16, val), types::I16)) + } } - (ConstantType::I8, Type::Primitive(PrimitiveType::I32)) => { - Ok((builder.ins().sextend(types::I32, val), ConstantType::I32)) + (types::I8, Type::Primitive(PrimitiveType::I32)) => { + if val_is_signed { + Ok((builder.ins().sextend(types::I32, val), types::I32)) + } else { + Ok((builder.ins().uextend(types::I32, val), types::I32)) + } } - (ConstantType::I8, Type::Primitive(PrimitiveType::I64)) => { - Ok((builder.ins().sextend(types::I64, val), ConstantType::I64)) + (types::I8, Type::Primitive(PrimitiveType::I64)) => { + if val_is_signed { + Ok((builder.ins().sextend(types::I64, val), types::I64)) + } else { + Ok((builder.ins().uextend(types::I64, val), types::I64)) + } } - (ConstantType::I16, Type::Primitive(PrimitiveType::I16)) => Ok((val, val_type)), - (ConstantType::I16, Type::Primitive(PrimitiveType::I32)) => { - Ok((builder.ins().sextend(types::I32, val), ConstantType::I32)) + (types::I16, Type::Primitive(PrimitiveType::I16)) => Ok((val, val_type)), + (types::I16, Type::Primitive(PrimitiveType::I32)) => { + if val_is_signed { + Ok((builder.ins().sextend(types::I32, val), types::I32)) + } else { + Ok((builder.ins().uextend(types::I32, val), types::I32)) + } } - (ConstantType::I16, Type::Primitive(PrimitiveType::I64)) => { - Ok((builder.ins().sextend(types::I64, val), ConstantType::I64)) + (types::I16, Type::Primitive(PrimitiveType::I64)) => { + if val_is_signed { + Ok((builder.ins().sextend(types::I64, val), types::I64)) + } else { + Ok((builder.ins().uextend(types::I64, val), types::I64)) + } } - (ConstantType::I32, Type::Primitive(PrimitiveType::I32)) => Ok((val, val_type)), - (ConstantType::I32, Type::Primitive(PrimitiveType::I64)) => { - Ok((builder.ins().sextend(types::I64, val), ConstantType::I64)) + (types::I32, Type::Primitive(PrimitiveType::I32)) => Ok((val, val_type)), + (types::I32, Type::Primitive(PrimitiveType::I64)) => { + if val_is_signed { + Ok((builder.ins().sextend(types::I64, val), types::I64)) + } else { + Ok((builder.ins().uextend(types::I64, val), types::I64)) + } } - (ConstantType::I64, Type::Primitive(PrimitiveType::I64)) => Ok((val, val_type)), + (types::I64, Type::Primitive(PrimitiveType::I64)) => Ok((val, val_type)), - (ConstantType::U8, Type::Primitive(PrimitiveType::U8)) => Ok((val, val_type)), - (ConstantType::U8, Type::Primitive(PrimitiveType::U16)) => { - Ok((builder.ins().uextend(types::I16, val), ConstantType::U16)) + (types::I8, Type::Primitive(PrimitiveType::U8)) => Ok((val, val_type)), + (types::I8, Type::Primitive(PrimitiveType::U16)) => { + Ok((builder.ins().uextend(types::I16, val), types::I16)) } - (ConstantType::U8, Type::Primitive(PrimitiveType::U32)) => { - Ok((builder.ins().uextend(types::I32, val), ConstantType::U32)) + (types::I8, Type::Primitive(PrimitiveType::U32)) => { + Ok((builder.ins().uextend(types::I32, val), types::I32)) } - (ConstantType::U8, Type::Primitive(PrimitiveType::U64)) => { - Ok((builder.ins().uextend(types::I64, val), ConstantType::U64)) + (types::I8, Type::Primitive(PrimitiveType::U64)) => { + Ok((builder.ins().uextend(types::I64, val), types::I64)) } - (ConstantType::U16, Type::Primitive(PrimitiveType::U16)) => Ok((val, val_type)), - (ConstantType::U16, Type::Primitive(PrimitiveType::U32)) => { - Ok((builder.ins().uextend(types::I32, val), ConstantType::U32)) + (types::I16, Type::Primitive(PrimitiveType::U16)) => Ok((val, val_type)), + (types::I16, Type::Primitive(PrimitiveType::U32)) => { + Ok((builder.ins().uextend(types::I32, val), types::I32)) } - (ConstantType::U16, Type::Primitive(PrimitiveType::U64)) => { - Ok((builder.ins().uextend(types::I64, val), ConstantType::U64)) + (types::I16, Type::Primitive(PrimitiveType::U64)) => { + Ok((builder.ins().uextend(types::I64, val), types::I64)) } - (ConstantType::U32, Type::Primitive(PrimitiveType::U32)) => Ok((val, val_type)), - (ConstantType::U32, Type::Primitive(PrimitiveType::U64)) => { - Ok((builder.ins().uextend(types::I64, val), ConstantType::U64)) + (types::I32, Type::Primitive(PrimitiveType::U32)) => Ok((val, val_type)), + (types::I32, Type::Primitive(PrimitiveType::U64)) => { + Ok((builder.ins().uextend(types::I64, val), types::I64)) } - (ConstantType::U64, Type::Primitive(PrimitiveType::U64)) => Ok((val, val_type)), - - (ConstantType::Void, Type::Primitive(PrimitiveType::Void)) => { - Ok((val, val_type)) - } - - (ConstantType::U8, Type::Primitive(PrimitiveType::I16)) => { - Ok((builder.ins().uextend(types::I16, val), ConstantType::I16)) - } - (ConstantType::U8, Type::Primitive(PrimitiveType::I32)) => { - Ok((builder.ins().uextend(types::I32, val), ConstantType::I32)) - } - (ConstantType::U8, Type::Primitive(PrimitiveType::I64)) => { - Ok((builder.ins().uextend(types::I64, val), ConstantType::I64)) - } - (ConstantType::U16, Type::Primitive(PrimitiveType::I32)) => { - Ok((builder.ins().uextend(types::I32, val), ConstantType::I32)) - } - (ConstantType::U16, Type::Primitive(PrimitiveType::I64)) => { - Ok((builder.ins().uextend(types::I64, val), ConstantType::I64)) - } - (ConstantType::U32, Type::Primitive(PrimitiveType::I64)) => { - Ok((builder.ins().uextend(types::I64, val), ConstantType::I64)) - } + (types::I64, Type::Primitive(PrimitiveType::U64)) => Ok((val, val_type)), _ => Err(BackendError::InvalidTypeCast { - from: val_type.into(), + from: val_type, to: target_type, }), } } - Expression::Primitive(_, _, prim, mut vals) => { + Expression::Primitive(_, ret_type, prim, mut vals) => { let mut values = vec![]; let mut first_type = None; @@ -392,7 +399,7 @@ impl Backend { } Primitive::Minus => Ok((builder.ins().ineg(values[0]), first_type)), Primitive::Times => Ok((builder.ins().imul(values[0], values[1]), first_type)), - Primitive::Divide if first_type.is_signed() => { + Primitive::Divide if ret_type.is_signed() => { Ok((builder.ins().sdiv(values[0], values[1]), first_type)) } Primitive::Divide => Ok((builder.ins().udiv(values[0], values[1]), first_type)), @@ -403,7 +410,7 @@ impl Backend { Expression::FieldRef(_, _, _, _) => unimplemented!(), Expression::Block(_, _, mut exprs) => match exprs.pop() { - None => Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)), + None => Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE)), Some(last) => { for inner in exprs { // we can ignore all of these return values and such, because we @@ -431,20 +438,31 @@ impl Backend { .declare_data_in_func(string_data_id, builder.func); let name_ptr = builder.ins().symbol_value(types::I64, local_name_ref); - let (val, vtype) = self.compile_value_or_ref(var, variables, builder)?; + let var_type = var.type_of(); + let (val, _) = self.compile_value_or_ref(var, variables, builder)?; - let vtype_repr = builder.ins().iconst(types::I64, vtype as i64); + let (repr_val, casted_val) = match var_type { + Type::Structure(_) => (ConstantType::I64 as i64, val), + Type::Function(_, _) => (ConstantType::I64 as i64, val), + Type::Primitive(pt) => { + let constant_type = pt.into(); - let casted_val = match vtype { - ConstantType::U64 | ConstantType::I64 | ConstantType::Void => val, - ConstantType::I8 | ConstantType::I16 | ConstantType::I32 => { - builder.ins().sextend(types::I64, val) - } - ConstantType::U8 | ConstantType::U16 | ConstantType::U32 => { - builder.ins().uextend(types::I64, val) + let new_val = match constant_type { + ConstantType::U64 | ConstantType::I64 | ConstantType::Void => val, + ConstantType::I8 | ConstantType::I16 | ConstantType::I32 => { + builder.ins().sextend(types::I64, val) + } + ConstantType::U8 | ConstantType::U16 | ConstantType::U32 => { + builder.ins().uextend(types::I64, val) + } + }; + + (constant_type as i64, new_val) } }; + let vtype_repr = builder.ins().iconst(types::I64, repr_val); + // Finally, we can generate the call to print. let print_func_ref = self.runtime_functions.include_runtime_function( "print", @@ -455,12 +473,11 @@ impl Backend { print_func_ref, &[buffer_ptr, name_ptr, vtype_repr, casted_val], ); - Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)) + Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE)) } Expression::Bind(_, name, _, expr) => { let (value, value_type) = self.compile_expression(*expr, variables, builder)?; - let ir_type = ir::Type::from(value_type); let variable = self.generate_local(); match variables.get(&name) { @@ -468,7 +485,7 @@ impl Backend { let pointer = self.module.target_config().pointer_type(); let pointer_to = builder.ins().symbol_value(pointer, *global_value); builder.ins().store(MemFlags::new(), value, pointer_to, 0); - Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)) + Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE)) } Some(ReferenceBuilder::Argument(_, _)) => { @@ -480,10 +497,10 @@ impl Backend { } None => { - builder.declare_var(variable, ir_type); + builder.declare_var(variable, value_type); builder.def_var(variable, value); variables.insert(name, ReferenceBuilder::Local(value_type, variable)); - Ok((builder.ins().iconst(types::I64, 0), ConstantType::Void)) + Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE)) } } } @@ -511,21 +528,10 @@ impl Backend { let results = builder.inst_results(call); match results { - [] => Ok(( - builder.ins().iconst(types::I64, 0), - ConstantType::Void, - )), - [result] => match result_type { - Type::Primitive(ct) => Ok((*result, ct.into())), - Type::Function(_, rt) => match *rt { - Type::Function(_, _) => { - panic!("function returns a function?") - } - Type::Structure(_) => unimplemented!(), - Type::Primitive(ct) => Ok((*result, ct.into())), - }, - Type::Structure(_) => unimplemented!(), - }, + [] => Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE)), + [result] => { + Ok((*result, self.translate_type(&result_type).value_type)) + } _ => panic!("don't support multi-value returns yet"), } } @@ -544,7 +550,7 @@ impl Backend { value_or_ref: ValueOrRef, variables: &HashMap, builder: &mut FunctionBuilder, - ) -> Result<(entities::Value, ConstantType), BackendError> { + ) -> Result<(entities::Value, types::Type), BackendError> { match value_or_ref { ValueOrRef::Value(_, _, val) => match val { Value::I8(_, v) => { @@ -555,49 +561,31 @@ impl Backend { // negative number for us. Which sets the high bits, which makes Cranelift unhappy. // So first we cast the i8 as u8, to get rid of the whole concept of sign extension, // and *then* we cast to i64. - Ok(( - builder.ins().iconst(types::I8, v as u8 as i64), - ConstantType::I8, - )) + Ok((builder.ins().iconst(types::I8, v as u8 as i64), types::I8)) } Value::I16(_, v) => Ok(( // see above note for the "... as ... as" builder.ins().iconst(types::I16, v as u16 as i64), - ConstantType::I16, + types::I16, )), Value::I32(_, v) => Ok(( // see above note for the "... as ... as" builder.ins().iconst(types::I32, v as u32 as i64), - ConstantType::I32, + types::I32, )), - Value::I64(_, v) => Ok((builder.ins().iconst(types::I64, v), ConstantType::I64)), - Value::U8(_, v) => { - Ok((builder.ins().iconst(types::I8, v as i64), ConstantType::U8)) - } - Value::U16(_, v) => Ok(( - builder.ins().iconst(types::I16, v as i64), - ConstantType::U16, - )), - Value::U32(_, v) => Ok(( - builder.ins().iconst(types::I32, v as i64), - ConstantType::U32, - )), - Value::U64(_, v) => Ok(( - builder.ins().iconst(types::I64, v as i64), - ConstantType::U64, - )), - Value::Void => Ok((builder.ins().iconst(types::I64, 0i64), ConstantType::Void)), + Value::I64(_, v) => Ok((builder.ins().iconst(types::I64, v), types::I64)), + Value::U8(_, v) => Ok((builder.ins().iconst(types::I8, v as i64), types::I8)), + Value::U16(_, v) => Ok((builder.ins().iconst(types::I16, v as i64), types::I16)), + Value::U32(_, v) => Ok((builder.ins().iconst(types::I32, v as i64), types::I32)), + Value::U64(_, v) => Ok((builder.ins().iconst(types::I64, v as i64), types::I64)), + Value::Void => Ok((builder.ins().iconst(types::I64, 0i64), VOID_REPR_TYPE)), }, ValueOrRef::Ref(_, _, name) => match variables.get(&name) { None => Err(BackendError::VariableLookupFailure(name)), Some(ReferenceBuilder::Global(ty, gv)) => { let pointer_to = self.module.target_config().pointer_type(); let pointer_value = builder.ins().symbol_value(pointer_to, *gv); - let cranelift_type = ir::Type::from(*ty); - let value = - builder - .ins() - .load(cranelift_type, MemFlags::new(), pointer_value, 0); + let value = builder.ins().load(*ty, MemFlags::new(), pointer_value, 0); Ok((value, *ty)) } diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 8d86fdf..456aa91 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -257,6 +257,16 @@ impl Type { pub fn is_printable(&self) -> bool { matches!(self, Type::Primitive(_)) } + + /// Returns true if the variable is signed. + pub fn is_signed(&self) -> bool { + matches!( + self, + Type::Primitive( + PrimitiveType::I8 | PrimitiveType::I16 | PrimitiveType::I32 | PrimitiveType::I64 + ) + ) + } } impl From for Type { diff --git a/src/ir/fields.rs b/src/ir/fields.rs index 71a541c..0c1b689 100644 --- a/src/ir/fields.rs +++ b/src/ir/fields.rs @@ -1,12 +1,23 @@ +use cranelift_module::DataDescription; use internment::ArcIntern; use std::fmt; -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone)] pub struct Fields { ordering: FieldOrdering, + total_size: usize, + cranelift_description: Option, fields: Vec<(ArcIntern, T)>, } +impl PartialEq for Fields { + fn eq(&self, other: &Self) -> bool { + self.ordering == other.ordering && self.fields == other.fields + } +} + +impl Eq for Fields {} + impl fmt::Debug for Fields { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Fields:")?; @@ -29,6 +40,8 @@ impl Fields { pub fn new(ordering: FieldOrdering) -> Fields { Fields { ordering, + total_size: 0, + cranelift_description: None, fields: vec![], } } @@ -38,6 +51,7 @@ impl Fields { } pub fn insert(&mut self, name: ArcIntern, t: T) { + self.total_size += 8; self.fields.push((name, t)); } @@ -54,6 +68,8 @@ impl Fields { pub fn map T2>(self, f: F) -> Fields { Fields { ordering: self.ordering, + total_size: self.total_size, + cranelift_description: self.cranelift_description, fields: self.fields.into_iter().map(|(n, t)| (n, f(t))).collect(), } } @@ -83,10 +99,6 @@ impl Fields { self.fields.iter().map(|(x, y)| (x, y)) } - pub fn into_iter(self) -> impl Iterator, T)> { - self.fields.into_iter() - } - pub fn field_names(&self) -> impl Iterator> { self.fields.iter().map(|(n, _)| n) } @@ -98,4 +110,22 @@ impl Fields { pub fn types_mut(&mut self) -> impl Iterator { self.fields.iter_mut().map(|(_, x)| x) } + + pub fn blank_data(&mut self) -> &DataDescription { + self.cranelift_description.get_or_insert_with(|| { + let mut cranelift_description = DataDescription::new(); + cranelift_description.set_align(8); + cranelift_description.define_zeroinit(self.total_size); + cranelift_description + }) + } +} + +impl IntoIterator for Fields { + type Item = (ArcIntern, T); + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.fields.into_iter() + } } diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs index fb139d9..9ad3e09 100644 --- a/src/syntax/tokens.rs +++ b/src/syntax/tokens.rs @@ -206,14 +206,6 @@ impl From for cranelift_codegen::ir::Type { } impl ConstantType { - /// Returns true if the given type is (a) numeric and (b) signed; - pub fn is_signed(&self) -> bool { - matches!( - self, - ConstantType::I8 | ConstantType::I16 | ConstantType::I32 | ConstantType::I64 - ) - } - /// Return the set of types that can be safely casted into this type. pub fn safe_casts_to(self) -> Vec { match self { -- 2.53.0 From fab5a230f1ac5f12e2727c41054cf015d12aff38 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Tue, 2 Apr 2024 21:00:05 -0700 Subject: [PATCH 38/59] move towards structure construction / deconstruction --- src/backend/into_crane.rs | 26 ++++++++++++++++++++++++-- src/ir/ast.rs | 6 +++--- src/ir/eval.rs | 2 +- src/ir/fields.rs | 14 ++++++++++++++ src/ir/pretty.rs | 2 +- src/type_infer/convert.rs | 1 + src/type_infer/finalize.rs | 3 ++- 7 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index 644af63..a3aa10f 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -406,8 +406,30 @@ impl Backend { } } - Expression::Construct(_, _, _, _) => unimplemented!(), - Expression::FieldRef(_, _, _, _) => unimplemented!(), + Expression::Construct(_, ty, name, fields) => { + let Type::Structure(fields) = ty else { + panic!("Got to backend with non-structure type in structure construction?!"); + }; + + unimplemented!() + } + + Expression::FieldRef(_, _, struct_type, val, field) => { + let (structure, _) = self.compile_value_or_ref(val, variables, builder)?; + + let Type::Structure(fields) = struct_type else { + panic!("Got to backend with non-structure type in field reference?!"); + }; + + let Some((field_type, offset)) = fields.field_type_and_offset(&field) else { + panic!("Got to backend with invalid field for structure type?!"); + }; + + let field_cranelift_type = self.translate_type(field_type).value_type; + + let value = builder.ins().load(field_cranelift_type, MemFlags::new(), structure, offset); + Ok((value, field_cranelift_type)) + } Expression::Block(_, _, mut exprs) => match exprs.pop() { None => Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE)), diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 456aa91..0e58076 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -113,7 +113,7 @@ pub enum Expression { ArcIntern, HashMap, ValueOrRef>, ), - FieldRef(Location, Type, ValueOrRef, ArcIntern), + FieldRef(Location, Type, Type, ValueOrRef, ArcIntern), Block(Location, Type, Vec>), Print(Location, ValueOrRef), Call(Location, Type, Box>, Vec>), @@ -129,7 +129,7 @@ impl Expression { Expression::Cast(_, t, _) => t.clone(), Expression::Primitive(_, t, _, _) => t.clone(), Expression::Construct(_, t, _, _) => t.clone(), - Expression::FieldRef(_, t, _, _) => t.clone(), + Expression::FieldRef(_, t, _, _, _) => t.clone(), Expression::Block(_, t, _) => t.clone(), Expression::Print(_, _) => Type::void(), Expression::Call(_, t, _, _) => t.clone(), @@ -145,7 +145,7 @@ impl Expression { Expression::Cast(l, _, _) => l, Expression::Primitive(l, _, _, _) => l, Expression::Construct(l, _, _, _) => l, - Expression::FieldRef(l, _, _, _) => l, + Expression::FieldRef(l, _, _, _, _) => l, Expression::Block(l, _, _) => l, Expression::Print(l, _) => l, Expression::Call(l, _, _, _) => l, diff --git a/src/ir/eval.rs b/src/ir/eval.rs index 6c8b01c..d7462ee 100644 --- a/src/ir/eval.rs +++ b/src/ir/eval.rs @@ -91,7 +91,7 @@ where Ok(Value::Structure(Some(name.clone()), result_fields)) } - Expression::FieldRef(loc, _, valref, field) => match valref.eval(env)? { + Expression::FieldRef(loc, _, _, valref, field) => match valref.eval(env)? { Value::Structure(oname, mut fields) => match fields.remove(field) { None => Err(EvalError::NoFieldForValue( loc.clone(), diff --git a/src/ir/fields.rs b/src/ir/fields.rs index 0c1b689..adc624d 100644 --- a/src/ir/fields.rs +++ b/src/ir/fields.rs @@ -119,6 +119,20 @@ impl Fields { cranelift_description }) } + + pub fn field_type_and_offset(&self, field: &ArcIntern) -> Option<(&T, i32)> { + let mut offset = 0; + + for (current, ty) in self.fields.iter() { + if current == field { + return Some((ty, offset)); + } + + offset += 8; + } + + None + } } impl IntoIterator for Fields { diff --git a/src/ir/pretty.rs b/src/ir/pretty.rs index b407059..129d8f9 100644 --- a/src/ir/pretty.rs +++ b/src/ir/pretty.rs @@ -73,7 +73,7 @@ impl Expression { .braces(); allocator.text(name.to_string()).append(inner) } - Expression::FieldRef(_, _, val, field) => val.pretty(allocator).append( + Expression::FieldRef(_, _, _, val, field) => val.pretty(allocator).append( allocator .text(".") .append(allocator.text(field.to_string())), diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index c6c9a88..0658d95 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -338,6 +338,7 @@ fn convert_expression( let result = ir::Expression::FieldRef( loc.clone(), result_type.clone(), + etype.clone(), val_or_ref, field.clone().intern(), ); diff --git a/src/type_infer/finalize.rs b/src/type_infer/finalize.rs index 03edec2..8949c26 100644 --- a/src/type_infer/finalize.rs +++ b/src/type_infer/finalize.rs @@ -73,9 +73,10 @@ fn finalize_expression( .collect(), ), - Expression::FieldRef(loc, ty, valref, field) => Expression::FieldRef( + Expression::FieldRef(loc, ty, struct_type, valref, field) => Expression::FieldRef( loc, finalize_type(ty, resolutions), + finalize_type(struct_type, resolutions), finalize_val_or_ref(valref, resolutions), field, ), -- 2.53.0 From 5f2fc7cb344db54a48308075a1756228d360f31f Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Wed, 3 Apr 2024 20:55:49 -0700 Subject: [PATCH 39/59] ngrc version works? --- examples/basic/broken0001.ngr | 2 ++ examples/basic/broken0003.ngr | 13 +++++++++ runtime/rts.c | 18 ++++++++++++ src/backend.rs | 30 +++++++++++++++++--- src/backend/into_crane.rs | 52 ++++++++++++++++++++++++++++++++--- src/backend/runtime.rs | 7 +++++ src/ir/fields.rs | 4 +++ src/type_infer/convert.rs | 2 +- 8 files changed, 119 insertions(+), 9 deletions(-) create mode 100644 examples/basic/broken0003.ngr diff --git a/examples/basic/broken0001.ngr b/examples/basic/broken0001.ngr index 360380a..76008d0 100644 --- a/examples/basic/broken0001.ngr +++ b/examples/basic/broken0001.ngr @@ -3,6 +3,8 @@ struct Point { y: u64; } +v = 1u64; + function getX(p: Point) -> u64 p.x; diff --git a/examples/basic/broken0003.ngr b/examples/basic/broken0003.ngr new file mode 100644 index 0000000..7fe0600 --- /dev/null +++ b/examples/basic/broken0003.ngr @@ -0,0 +1,13 @@ +struct Point { + x: u64; + y: u64; +} + +test = Point { + x: 1; + y: 2; +}; + +foo = test.x; + +print foo; \ No newline at end of file diff --git a/runtime/rts.c b/runtime/rts.c index dbcd759..7649ca8 100644 --- a/runtime/rts.c +++ b/runtime/rts.c @@ -1,7 +1,13 @@ #include #include +#include +#include #include +#define HEAP_SIZE (1024 * 1024) + +void *__global_allocation_pointer__ = NULL; + void print(char *_ignore, char *variable_name, int64_t vtype, int64_t value) { switch(vtype) { case /* U8 = */ 10: @@ -39,6 +45,18 @@ void print(char *_ignore, char *variable_name, int64_t vtype, int64_t value) { extern void gogogo(); int main(int argc, char **argv) { + __global_allocation_pointer__ = malloc(HEAP_SIZE); + if(__global_allocation_pointer__ == NULL) { + printf("ERROR: Couldn't allocation heap space."); + return 1; + } + + if(memset(__global_allocation_pointer__, 0, HEAP_SIZE) != __global_allocation_pointer__) { + printf("ERROR: Weird return trying to zero out heap."); + return 2; + } + gogogo(); + return 0; } \ No newline at end of file diff --git a/src/backend.rs b/src/backend.rs index 6047134..2b475e4 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -89,7 +89,7 @@ impl Backend { let mut module = JITModule::new(builder); let runtime_functions = RuntimeFunctions::new(&platform, &mut module)?; - Ok(Backend { + let mut retval = Backend { module, data_ctx: DataDescription::new(), runtime_functions, @@ -99,7 +99,18 @@ impl Backend { output_buffer, platform: Triple::host(), next_variable: 23, - }) + }; + + let alloc = "__global_allocation_pointer__".to_string(); + let id = retval + .module + .declare_data(&alloc, Linkage::Import, true, false)?; + retval.defined_symbols.insert( + ArcIntern::new(alloc), + (id, retval.module.target_config().pointer_type()), + ); + + Ok(retval) } /// Given a compiled function ID, get a pointer to where that function was written @@ -130,7 +141,7 @@ impl Backend { let mut module = ObjectModule::new(object_builder); let runtime_functions = RuntimeFunctions::new(&platform, &mut module)?; - Ok(Backend { + let mut retval = Backend { module, data_ctx: DataDescription::new(), runtime_functions, @@ -140,7 +151,18 @@ impl Backend { output_buffer: None, platform, next_variable: 23, - }) + }; + + let alloc = "__global_allocation_pointer__".to_string(); + let id = retval + .module + .declare_data(&alloc, Linkage::Import, true, false)?; + retval.defined_symbols.insert( + ArcIntern::new(alloc), + (id, retval.module.target_config().pointer_type()), + ); + + Ok(retval) } /// Given all the functions defined, return the bytes the object file should contain. diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index a3aa10f..8e9456f 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -406,12 +406,53 @@ impl Backend { } } - Expression::Construct(_, ty, name, fields) => { - let Type::Structure(fields) = ty else { + Expression::Construct(_, ty, _, fields) => { + let Type::Structure(type_fields) = ty else { panic!("Got to backend with non-structure type in structure construction?!"); }; - unimplemented!() + let global_allocator = ArcIntern::new("__global_allocation_pointer__".to_string()); + let Some(ReferenceBuilder::Global(_, allocator_variable)) = + variables.get(&global_allocator) + else { + panic!("Couldn't find global allocation pointer"); + }; + + let pointer_to = self.module.target_config().pointer_type(); + let allocator_pointer = builder.ins().symbol_value(pointer_to, *allocator_variable); + let structure = + builder + .ins() + .load(pointer_to, MemFlags::new(), allocator_pointer, 0); + let structure_size = builder + .ins() + .iconst(pointer_to, type_fields.object_size() as i64); + let updated_allocator_value = builder.ins().iadd(structure, structure_size); + builder.ins().store( + MemFlags::new(), + updated_allocator_value, + allocator_pointer, + 0, + ); + + for (field_name, field_value) in fields.into_iter() { + let (field_value, field_cranelift_type) = + self.compile_value_or_ref(field_value, variables, builder)?; + let Some((field_internal_type, offset)) = + type_fields.field_type_and_offset(&field_name) + else { + panic!("Got to backend with mismatched construction and type definition"); + }; + assert_eq!( + field_cranelift_type, + self.translate_type(field_internal_type).value_type + ); + builder + .ins() + .store(MemFlags::new(), field_value, structure, offset); + } + + Ok((structure, self.module.target_config().pointer_type())) } Expression::FieldRef(_, _, struct_type, val, field) => { @@ -427,7 +468,10 @@ impl Backend { let field_cranelift_type = self.translate_type(field_type).value_type; - let value = builder.ins().load(field_cranelift_type, MemFlags::new(), structure, offset); + let value = + builder + .ins() + .load(field_cranelift_type, MemFlags::new(), structure, offset); Ok((value, field_cranelift_type)) } diff --git a/src/backend/runtime.rs b/src/backend/runtime.rs index 6362176..6e4b437 100644 --- a/src/backend/runtime.rs +++ b/src/backend/runtime.rs @@ -91,7 +91,14 @@ impl RuntimeFunctions { /// one; both to reduce the chance that they deviate, and to reduce overall /// maintenance burden. pub fn register_jit_implementations(builder: &mut JITBuilder) { + let allocation_pointer = unsafe { + std::alloc::alloc_zeroed( + std::alloc::Layout::from_size_align(1024 * 1024, 1024 * 1024) + .expect("reasonable layout is reasonable"), + ) + }; builder.symbol("print", runtime_print as *const u8); + builder.symbol("__global_allocation_pointer__", allocation_pointer); } } diff --git a/src/ir/fields.rs b/src/ir/fields.rs index adc624d..f59ae43 100644 --- a/src/ir/fields.rs +++ b/src/ir/fields.rs @@ -133,6 +133,10 @@ impl Fields { None } + + pub fn object_size(&self) -> usize { + self.total_size + } } impl IntoIterator for Fields { diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 0658d95..85f7224 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -183,7 +183,7 @@ fn convert_statement( let iname = ArcIntern::new(name.to_string()); let final_name = renames .get(&iname) - .map(Clone::clone) + .cloned() .unwrap_or_else(|| iname.clone()); let varty = bindings .get(&final_name) -- 2.53.0 From 8479a84e07d2294bd3bea2ff3f5e195fb6976bfe Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Thu, 11 Apr 2024 08:57:50 -0700 Subject: [PATCH 40/59] basic structures work in the jit --- examples/basic/{broken0001.ngr => struct0001.ngr} | 2 +- src/backend/into_crane.rs | 1 + src/backend/runtime.rs | 10 ++++++++-- src/bin/ngrun.rs | 11 +++++++++-- 4 files changed, 19 insertions(+), 5 deletions(-) rename examples/basic/{broken0001.ngr => struct0001.ngr} (93%) diff --git a/examples/basic/broken0001.ngr b/examples/basic/struct0001.ngr similarity index 93% rename from examples/basic/broken0001.ngr rename to examples/basic/struct0001.ngr index 76008d0..dd6f260 100644 --- a/examples/basic/broken0001.ngr +++ b/examples/basic/struct0001.ngr @@ -21,7 +21,7 @@ function slope(p1, p2) -> u64 (getY(p2) - p1.y) / (getX(p2) - p1.x); origin = newPoint(0, 0); -farther = newPoint(4, 4); +farther = newPoint(2, 4); mySlope = slope(origin, farther); print mySlope; \ No newline at end of file diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index 8e9456f..8ab635c 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -199,6 +199,7 @@ impl Backend { let func_id = match self.defined_functions.entry(interned_name) { hash_map::Entry::Occupied(entry) => *entry.get(), hash_map::Entry::Vacant(vac) => { + tracing::warn!(name = ?function_name, "compiling undeclared function"); let func_id = self.module.declare_function( function_name, Linkage::Export, diff --git a/src/backend/runtime.rs b/src/backend/runtime.rs index 6e4b437..3b0b18d 100644 --- a/src/backend/runtime.rs +++ b/src/backend/runtime.rs @@ -3,6 +3,7 @@ use cranelift_codegen::isa::CallConv; use cranelift_jit::JITBuilder; use cranelift_module::{FuncId, Linkage, Module, ModuleResult}; use std::collections::HashMap; +use std::alloc::Layout; use std::ffi::CStr; use std::fmt::Write; use target_lexicon::Triple; @@ -93,12 +94,17 @@ impl RuntimeFunctions { pub fn register_jit_implementations(builder: &mut JITBuilder) { let allocation_pointer = unsafe { std::alloc::alloc_zeroed( - std::alloc::Layout::from_size_align(1024 * 1024, 1024 * 1024) + Layout::from_size_align(1024 * 1024, 1024 * 1024) .expect("reasonable layout is reasonable"), ) }; + let allocation_pointer_pointer = unsafe { + let res = std::alloc::alloc(Layout::for_value(&allocation_pointer)) as *mut *mut u8; + *res = allocation_pointer; + res as *const u8 + }; builder.symbol("print", runtime_print as *const u8); - builder.symbol("__global_allocation_pointer__", allocation_pointer); + builder.symbol("__global_allocation_pointer__", allocation_pointer_pointer); } } diff --git a/src/bin/ngrun.rs b/src/bin/ngrun.rs index c40cfbb..c0d79da 100644 --- a/src/bin/ngrun.rs +++ b/src/bin/ngrun.rs @@ -1,10 +1,12 @@ use clap::Parser; use codespan_reporting::files::SimpleFiles; +use cranelift_codegen::timing::compile; use ngr::backend::Backend; use ngr::eval::Value; use ngr::syntax; use ngr::type_infer::TypeInferenceResult; use pretty::termcolor::StandardStream; +use tracing_subscriber::prelude::*; #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] @@ -42,7 +44,10 @@ fn jit(ir: ngr::ir::Program) -> Result emit(e.into()), - Ok(compiled_function) => compiled_function(), + Ok(compiled_function) => { + compiled_function() + } } } -- 2.53.0 From cfcb3f042bf30e56dc8ee3605660a2a272e4e98a Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Thu, 11 Apr 2024 09:15:08 -0700 Subject: [PATCH 41/59] Fix broken struct type inference. --- examples/basic/{broken0003.ngr => struct0002.ngr} | 0 src/type_infer/convert.rs | 4 +++- 2 files changed, 3 insertions(+), 1 deletion(-) rename examples/basic/{broken0003.ngr => struct0002.ngr} (100%) diff --git a/examples/basic/broken0003.ngr b/examples/basic/struct0002.ngr similarity index 100% rename from examples/basic/broken0003.ngr rename to examples/basic/struct0002.ngr diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 85f7224..b16a1e4 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -307,10 +307,12 @@ fn convert_expression( merge_prereq(&mut prereqs, prereq); } + let result_type = ir::TypeOrVar::Structure(type_fields); + constraint_db.push(Constraint::NamedTypeIs( loc.clone(), name.clone().intern(), - ir::TypeOrVar::Structure(type_fields), + result_type.clone(), )); let result = ir::Expression::Construct(loc, result_type.clone(), name.intern(), result_fields); -- 2.53.0 From 85407c8f480e28e8933e929cf20e9abbf9f1da3f Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Thu, 11 Apr 2024 09:15:19 -0700 Subject: [PATCH 42/59] Formatting. --- src/backend/runtime.rs | 2 +- src/bin/ngrun.rs | 4 +--- src/type_infer/convert.rs | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/backend/runtime.rs b/src/backend/runtime.rs index 3b0b18d..5d0e25f 100644 --- a/src/backend/runtime.rs +++ b/src/backend/runtime.rs @@ -2,8 +2,8 @@ use cranelift_codegen::ir::{types, AbiParam, FuncRef, Function, Signature}; use cranelift_codegen::isa::CallConv; use cranelift_jit::JITBuilder; use cranelift_module::{FuncId, Linkage, Module, ModuleResult}; -use std::collections::HashMap; use std::alloc::Layout; +use std::collections::HashMap; use std::ffi::CStr; use std::fmt::Write; use target_lexicon::Triple; diff --git a/src/bin/ngrun.rs b/src/bin/ngrun.rs index c0d79da..e922e9d 100644 --- a/src/bin/ngrun.rs +++ b/src/bin/ngrun.rs @@ -112,8 +112,6 @@ fn main() { match jit(ir) { Err(e) => emit(e.into()), - Ok(compiled_function) => { - compiled_function() - } + Ok(compiled_function) => compiled_function(), } } diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index b16a1e4..5d263eb 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -296,7 +296,6 @@ fn convert_expression( let mut result_fields = HashMap::new(); let mut type_fields = ir::Fields::default(); let mut prereqs = vec![]; - let result_type = ir::TypeOrVar::new(); for (name, syntax_expr) in fields.into_iter() { let (ir_expr, expr_type) = -- 2.53.0 From a3ce53bca2262c7e6ef2192c1f9c7028c3a7b118 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Thu, 11 Apr 2024 09:22:28 -0700 Subject: [PATCH 43/59] A more complicated structure test. --- examples/basic/struct0003.ngr | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 examples/basic/struct0003.ngr diff --git a/examples/basic/struct0003.ngr b/examples/basic/struct0003.ngr new file mode 100644 index 0000000..ac46800 --- /dev/null +++ b/examples/basic/struct0003.ngr @@ -0,0 +1,27 @@ +struct Point { + x: u64; + y: u64; +} + +struct Line { + start: Point; + end: Point; +} + +function slope(l) -> u64 + (l.end.y - l.start.y) / (l.end.x - l.start.x); + +test = Line { + start: Point { + x: 1; + y: 2; + }; + end: Point { + x: 2; + y: 4; + }; +}; + +foo = slope(test); + +print foo; \ No newline at end of file -- 2.53.0 From 632770f4f437a52c6c8604ab9bcdebdeb42c7bd3 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Thu, 11 Apr 2024 09:25:28 -0700 Subject: [PATCH 44/59] stray import --- src/bin/ngrun.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bin/ngrun.rs b/src/bin/ngrun.rs index e922e9d..80a06bb 100644 --- a/src/bin/ngrun.rs +++ b/src/bin/ngrun.rs @@ -1,6 +1,5 @@ use clap::Parser; use codespan_reporting::files::SimpleFiles; -use cranelift_codegen::timing::compile; use ngr::backend::Backend; use ngr::eval::Value; use ngr::syntax; -- 2.53.0 From f915db9337acad8d5ad5d82cc13ba0f03213b341 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Thu, 11 Apr 2024 10:03:11 -0700 Subject: [PATCH 45/59] Fix the results of binds problem. --- src/backend/eval.rs | 17 ++++++++--------- src/backend/into_crane.rs | 4 ++++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/backend/eval.rs b/src/backend/eval.rs index f1c3810..82242bc 100644 --- a/src/backend/eval.rs +++ b/src/backend/eval.rs @@ -129,15 +129,14 @@ proptest::proptest! { let basic_result = basic_result.map(|x| x.replace('\n', "\r\n")); if !matches!(basic_result, Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))) { -// use pretty::{DocAllocator, Pretty}; -// let allocator = pretty::BoxAllocator; -// allocator -// .text("---------------") -// .append(allocator.hardline()) -// .append(program.pretty(&allocator)) -// .1 -// .render_colored(70, pretty::termcolor::StandardStream::stdout(pretty::termcolor::ColorChoice::Auto)) -// .expect("rendering works"); + //use pretty::DocAllocator; + //let allocator = pretty::Arena::new(); + //let result = allocator.text("-------------") + // .append(allocator.line()) + // .append(program.pretty(&allocator)) + // .append(allocator.line()); + //result.render_raw(70, &mut pretty::IoWrite::new(std::io::stdout())) + // .expect("rendering works"); let compiled_result = Backend::::eval(program); proptest::prop_assert_eq!(basic_result, compiled_result); diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index 8ab635c..724153a 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -363,6 +363,10 @@ impl Backend { (types::I64, Type::Primitive(PrimitiveType::U64)) => Ok((val, val_type)), + (types::I64, Type::Primitive(PrimitiveType::Void)) => { + Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE)) + } + _ => Err(BackendError::InvalidTypeCast { from: val_type, to: target_type, -- 2.53.0 From 3d8e0804bc22a8706caf2fb7251792e1c84e0efc Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Mon, 15 Apr 2024 15:28:06 -0700 Subject: [PATCH 46/59] Add more defaulting --- examples/basic/{test1.ngr => test0001.ngr} | 0 examples/basic/{test2.ngr => test0002.ngr} | 0 examples/basic/test0003.ngr | 1 + examples/basic/test0004.ngr | 4 +++ src/type_infer/solve.rs | 29 +++++++++++++++++----- 5 files changed, 28 insertions(+), 6 deletions(-) rename examples/basic/{test1.ngr => test0001.ngr} (100%) rename examples/basic/{test2.ngr => test0002.ngr} (100%) create mode 100644 examples/basic/test0003.ngr create mode 100644 examples/basic/test0004.ngr diff --git a/examples/basic/test1.ngr b/examples/basic/test0001.ngr similarity index 100% rename from examples/basic/test1.ngr rename to examples/basic/test0001.ngr diff --git a/examples/basic/test2.ngr b/examples/basic/test0002.ngr similarity index 100% rename from examples/basic/test2.ngr rename to examples/basic/test0002.ngr diff --git a/examples/basic/test0003.ngr b/examples/basic/test0003.ngr new file mode 100644 index 0000000..fb4df05 --- /dev/null +++ b/examples/basic/test0003.ngr @@ -0,0 +1 @@ +v = -2370389138213399653i64; \ No newline at end of file diff --git a/examples/basic/test0004.ngr b/examples/basic/test0004.ngr new file mode 100644 index 0000000..cc0deb6 --- /dev/null +++ b/examples/basic/test0004.ngr @@ -0,0 +1,4 @@ +function u (t,h,z,u,a,f,c) 27u8; +function s (p,y,k) 10318938949979263534u64; +o = -98i8; +print o; \ No newline at end of file diff --git a/src/type_infer/solve.rs b/src/type_infer/solve.rs index e0b69a7..b18d284 100644 --- a/src/type_infer/solve.rs +++ b/src/type_infer/solve.rs @@ -847,22 +847,39 @@ pub fn solve_constraints( if !changed_something { let mut addendums = vec![]; - new_constraints.retain(|x| { - if let Constraint::ConstantNumericType(loc, t) = x { + macro_rules! default_type { + ($addendums: ident, $loc: ident, $t: ident) => { let resty = TypeOrVar::Primitive(PrimitiveType::U64); - addendums.push(Constraint::Equivalent( - loc.clone(), - t.clone(), + $addendums.push(Constraint::Equivalent( + $loc.clone(), + $t.clone(), resty.clone(), )); - warnings.push(TypeInferenceWarning::DefaultedTo(loc.clone(), resty)); + warnings.push(TypeInferenceWarning::DefaultedTo($loc.clone(), resty)); tracing::trace!("Adding number equivalence"); + }; + } + + new_constraints.retain(|x| { + if let Constraint::ConstantNumericType(loc, t) = x { + default_type!(addendums, loc, t); false } else { true } }); + if addendums.is_empty() { + new_constraints.retain(|x| { + if let Constraint::IsSomething(loc, t) = x { + default_type!(addendums, loc, t); + false + } else { + true + } + }); + } + if addendums.is_empty() { if errors.is_empty() { errors = new_constraints -- 2.53.0 From 763a8952854119038ab617df31206a6890e1e033 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Mon, 15 Apr 2024 16:31:44 -0700 Subject: [PATCH 47/59] Allow chained equals. --- examples/basic/test0005.ngr | 2 ++ src/backend/eval.rs | 16 +++++------ src/backend/into_crane.rs | 4 +-- src/ir/eval.rs | 4 +-- src/repl.rs | 10 ++++--- src/syntax.rs | 54 +++++++++++++++++++------------------ src/syntax/arbitrary.rs | 6 ++--- src/syntax/ast.rs | 11 ++++---- src/syntax/eval.rs | 12 ++++----- src/syntax/parser.lalrpop | 26 ++++++++++-------- src/syntax/pretty.rs | 12 ++++----- src/syntax/validate.rs | 37 +++++++++++++------------ src/type_infer/convert.rs | 19 +++++++------ 13 files changed, 114 insertions(+), 99 deletions(-) create mode 100644 examples/basic/test0005.ngr diff --git a/examples/basic/test0005.ngr b/examples/basic/test0005.ngr new file mode 100644 index 0000000..447c859 --- /dev/null +++ b/examples/basic/test0005.ngr @@ -0,0 +1,2 @@ +t = v = 5i64; +print t; \ No newline at end of file diff --git a/src/backend/eval.rs b/src/backend/eval.rs index 82242bc..fce0da9 100644 --- a/src/backend/eval.rs +++ b/src/backend/eval.rs @@ -129,14 +129,14 @@ proptest::proptest! { let basic_result = basic_result.map(|x| x.replace('\n', "\r\n")); if !matches!(basic_result, Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))) { - //use pretty::DocAllocator; - //let allocator = pretty::Arena::new(); - //let result = allocator.text("-------------") - // .append(allocator.line()) - // .append(program.pretty(&allocator)) - // .append(allocator.line()); - //result.render_raw(70, &mut pretty::IoWrite::new(std::io::stdout())) - // .expect("rendering works"); + use pretty::DocAllocator; + let allocator = pretty::Arena::new(); + let result = allocator.text("-------------") + .append(allocator.line()) + .append(program.pretty(&allocator)) + .append(allocator.line()); + result.render_raw(70, &mut pretty::IoWrite::new(std::io::stdout())) + .expect("rendering works"); let compiled_result = Backend::::eval(program); proptest::prop_assert_eq!(basic_result, compiled_result); diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index 724153a..cfcd47d 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -556,7 +556,6 @@ impl Backend { let pointer = self.module.target_config().pointer_type(); let pointer_to = builder.ins().symbol_value(pointer, *global_value); builder.ins().store(MemFlags::new(), value, pointer_to, 0); - Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE)) } Some(ReferenceBuilder::Argument(_, _)) => { @@ -571,9 +570,10 @@ impl Backend { builder.declare_var(variable, value_type); builder.def_var(variable, value); variables.insert(name, ReferenceBuilder::Local(value_type, variable)); - Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE)) } } + + Ok((value, value_type)) } Expression::Call(_, _, function, args) => { diff --git a/src/ir/eval.rs b/src/ir/eval.rs index d7462ee..63748b0 100644 --- a/src/ir/eval.rs +++ b/src/ir/eval.rs @@ -126,8 +126,8 @@ where Expression::Bind(_, name, _, value) => { let value = value.eval(env, stdout)?; - env.insert(name.clone(), value); - Ok(Value::Void) + env.insert(name.clone(), value.clone()); + Ok(value) } Expression::Call(loc, _, fun, args) => { diff --git a/src/repl.rs b/src/repl.rs index 1fbd501..58225f7 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,5 +1,5 @@ use crate::backend::{Backend, BackendError}; -use crate::syntax::{ConstantType, Location, ParserError, Statement, TopLevel}; +use crate::syntax::{ConstantType, Expression, Location, ParserError, Statement, TopLevel}; use crate::type_infer::TypeInferenceResult; use crate::util::scoped_map::ScopedMap; use codespan_reporting::diagnostic::Diagnostic; @@ -130,7 +130,7 @@ impl REPL { let syntax = TopLevel::parse(entry, source)?; let program = match syntax { - TopLevel::Statement(Statement::Binding(loc, name, expr)) => { + TopLevel::Statement(Statement::Expression(Expression::Binding(loc, name, expr))) => { // if this is a variable binding, and we've never defined this variable before, // we should tell cranelift about it. this is optimistic; if we fail to compile, // then we won't use this definition until someone tries again. @@ -142,7 +142,11 @@ impl REPL { crate::syntax::Program { items: vec![ - TopLevel::Statement(Statement::Binding(loc.clone(), name.clone(), expr)), + TopLevel::Statement(Statement::Expression(Expression::Binding( + loc.clone(), + name.clone(), + expr, + ))), TopLevel::Statement(Statement::Print(loc, name)), ], } diff --git a/src/syntax.rs b/src/syntax.rs index 3f89769..751c36d 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -285,32 +285,34 @@ fn order_of_operations() { assert_eq!( Program::from_str(muladd1).unwrap(), Program { - items: vec![TopLevel::Statement(Statement::Binding( - Location::new(testfile, 0..1), - Name::manufactured("x"), - Expression::Primitive( - Location::new(testfile, 6..7), - "+".to_string(), - vec![ - Expression::Value( - Location::new(testfile, 4..5), - Value::Number(None, None, 1), - ), - Expression::Primitive( - Location::new(testfile, 10..11), - "*".to_string(), - vec![ - Expression::Value( - Location::new(testfile, 8..9), - Value::Number(None, None, 2), - ), - Expression::Value( - Location::new(testfile, 12..13), - Value::Number(None, None, 3), - ), - ] - ) - ] + items: vec![TopLevel::Statement(Statement::Expression( + Expression::Binding( + Location::new(testfile, 0..1), + Name::manufactured("x"), + Box::new(Expression::Primitive( + Location::new(testfile, 6..7), + "+".to_string(), + vec![ + Expression::Value( + Location::new(testfile, 4..5), + Value::Number(None, None, 1), + ), + Expression::Primitive( + Location::new(testfile, 10..11), + "*".to_string(), + vec![ + Expression::Value( + Location::new(testfile, 8..9), + Value::Number(None, None, 2), + ), + Expression::Value( + Location::new(testfile, 12..13), + Value::Number(None, None, 3), + ), + ] + ) + ] + )) ) ))], } diff --git a/src/syntax/arbitrary.rs b/src/syntax/arbitrary.rs index cbe03ad..9abfa1f 100644 --- a/src/syntax/arbitrary.rs +++ b/src/syntax/arbitrary.rs @@ -72,11 +72,11 @@ impl Arbitrary for Program { genenv.bindings.insert(psi.name.clone(), psi.binding_type); items.push( expr.prop_map(move |expr| { - TopLevel::Statement(Statement::Binding( + TopLevel::Statement(Statement::Expression(Expression::Binding( Location::manufactured(), psi.name.clone(), - expr, - )) + Box::new(expr), + ))) }) .boxed(), ); diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 2fff204..f128ecb 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -99,7 +99,6 @@ impl fmt::Display for Name { /// thing, not if they are the exact same statement. #[derive(Clone, Debug)] pub enum Statement { - Binding(Location, Name, Expression), Print(Location, Name), Expression(Expression), } @@ -107,10 +106,6 @@ pub enum Statement { 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, @@ -139,6 +134,7 @@ pub enum Expression { Primitive(Location, String, Vec), Call(Location, Box, Vec), Block(Location, Vec), + Binding(Location, Name, Box), } impl PartialEq for Expression { @@ -176,6 +172,10 @@ impl PartialEq for Expression { Expression::Block(_, stmts2) => stmts1 == stmts2, _ => false, }, + Expression::Binding(_, name1, expr1) => match other { + Expression::Binding(_, name2, expr2) => name1 == name2 && expr1 == expr2, + _ => false, + }, } } } @@ -192,6 +192,7 @@ impl Expression { Expression::Primitive(loc, _, _) => loc, Expression::Call(loc, _, _) => loc, Expression::Block(loc, _) => loc, + Expression::Binding(loc, _, _) => loc, } } } diff --git a/src/syntax/eval.rs b/src/syntax/eval.rs index 506ed13..7546392 100644 --- a/src/syntax/eval.rs +++ b/src/syntax/eval.rs @@ -59,12 +59,6 @@ impl Statement { env: &mut ScopedMap, Value>, ) -> Result, EvalError> { match self { - Statement::Binding(_, name, value) => { - let actual_value = value.eval(stdout, env)?; - env.insert(name.clone().intern(), actual_value); - Ok(Value::Void) - } - Statement::Print(loc, name) => { let value = env .get(&name.clone().intern()) @@ -197,6 +191,12 @@ impl Expression { Ok(result) } + + Expression::Binding(_, name, value) => { + let actual_value = value.eval(stdout, env)?; + env.insert(name.clone().intern(), actual_value.clone()); + Ok(actual_value) + } } } } diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index 70534ea..3c37a92 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -148,16 +148,6 @@ Statements: Vec = { #[inline] Statement: Statement = { - // A statement can be a variable binding. Note, here, that we use this - // funny @L thing to get the source location before the variable, so that - // we can say that this statement spans across everything. - "> "=" => - Statement::Binding( - Location::new(file_idx, ls..le), - Name::new(v, Location::new(file_idx, ls..var_end)), - e, - ), - // A statement can just be a print statement. "print" "> => Statement::Print( @@ -194,13 +184,27 @@ Statement: Statement = { // parse something like "1 + 2 * 3", for example, versus "1 + 2 + 3" or // "1 * 2 + 3", and hopefully that'll help. Expression: Expression = { + BindingExpression, +} + +BindingExpression: Expression = { + // An expression can be a variable binding. Note, here, that we use this + // funny @L thing to get the source location before the variable, so that + // we can say that this statement spans across everything. + "> "=" => + Expression::Binding( + Location::new(file_idx, ls..le), + Name::new(v, Location::new(file_idx, ls..var_end)), + Box::new(e), + ), + ConstructorExpression, - AdditiveExpression, } ConstructorExpression: Expression = { "{" "}" => Expression::Constructor(Location::new(file_idx, s..e), name, fields), + AdditiveExpression, } FieldSetter: (Name, Expression) = { diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index 10fa97c..249c571 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -90,12 +90,6 @@ impl TopLevel { impl Statement { pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { match self { - Statement::Binding(_, var, expr) => allocator - .text(var.to_string()) - .append(allocator.space()) - .append(allocator.text("=")) - .append(allocator.space()) - .append(expr.pretty(allocator)), Statement::Print(_, var) => allocator .text("print") .append(allocator.space()) @@ -178,6 +172,12 @@ impl Expression { .append(allocator.text("}")) } }, + Expression::Binding(_, var, expr) => allocator + .text(var.to_string()) + .append(allocator.space()) + .append(allocator.text("=")) + .append(allocator.space()) + .append(expr.pretty(allocator)), } } } diff --git a/src/syntax/validate.rs b/src/syntax/validate.rs index d751578..3c868d0 100644 --- a/src/syntax/validate.rs +++ b/src/syntax/validate.rs @@ -156,25 +156,6 @@ impl Statement { let mut warnings = vec![]; match self { - Statement::Binding(loc, var, val) => { - // we're going to make the decision that a variable is not bound in the right - // hand side of its binding, which makes a lot of things easier. So we'll just - // immediately check the expression, and go from there. - let (mut exp_errors, mut exp_warnings) = val.validate(bound_variables); - - errors.append(&mut exp_errors); - warnings.append(&mut exp_warnings); - if let Some(original_binding_site) = bound_variables.get(&var.name) { - warnings.push(Warning::ShadowedVariable( - original_binding_site.clone(), - loc.clone(), - var.to_string(), - )); - } else { - bound_variables.insert(var.to_string(), loc.clone()); - } - } - Statement::Print(_, var) if bound_variables.contains_key(&var.name) => {} Statement::Print(loc, var) => { errors.push(Error::UnboundVariable(loc.clone(), var.to_string())) @@ -260,6 +241,24 @@ impl Expression { warnings.append(&mut local_warnings); } + (errors, warnings) + } + Expression::Binding(loc, var, val) => { + // we're going to make the decision that a variable is not bound in the right + // hand side of its binding, which makes a lot of things easier. So we'll just + // immediately check the expression, and go from there. + let (errors, mut warnings) = val.validate(variable_map); + + if let Some(original_binding_site) = variable_map.get(&var.name) { + warnings.push(Warning::ShadowedVariable( + original_binding_site.clone(), + loc.clone(), + var.to_string(), + )); + } else { + variable_map.insert(var.to_string(), loc.clone()); + } + (errors, warnings) } } diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 5d263eb..1fdf95d 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -195,14 +195,6 @@ fn convert_statement( ir::Expression::Print(loc.clone(), ir::ValueOrRef::Ref(loc, varty, final_name)) } - syntax::Statement::Binding(loc, name, expr) => { - let (expr, ty) = convert_expression(expr, constraint_db, renames, bindings); - let final_name = finalize_name(bindings, renames, name); - bindings.insert(final_name.clone(), ty.clone()); - - ir::Expression::Bind(loc, final_name, ty, Box::new(expr)) - } - syntax::Statement::Expression(e) => { convert_expression(e, constraint_db, renames, bindings).0 } @@ -464,6 +456,17 @@ fn convert_expression( ret_type, ) } + + syntax::Expression::Binding(loc, name, expr) => { + let (expr, ty) = convert_expression(*expr, constraint_db, renames, bindings); + let final_name = finalize_name(bindings, renames, name); + bindings.insert(final_name.clone(), ty.clone()); + + ( + ir::Expression::Bind(loc, final_name, ty.clone(), Box::new(expr)), + ty, + ) + } } } -- 2.53.0 From 7d4f182a67fa4136262e391984e3d25abb8d4304 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Tue, 16 Apr 2024 16:20:31 -0700 Subject: [PATCH 48/59] Clean up primitive handling, finally. --- src/backend/into_crane.rs | 208 ++++++++++++++++++++----------------- src/eval/primop.rs | 9 +- src/eval/primtype.rs | 8 ++ src/eval/value.rs | 11 ++ src/ir/arbitrary.rs | 29 +++++- src/ir/ast.rs | 13 +-- src/ir/eval.rs | 52 +++++----- src/ir/pretty.rs | 23 +--- src/repl.rs | 17 +-- src/syntax.rs | 56 +++++----- src/syntax/arbitrary.rs | 26 +++-- src/syntax/ast.rs | 57 ++++------ src/syntax/eval.rs | 66 +++++------- src/syntax/parser.lalrpop | 67 ++++++------ src/syntax/pretty.rs | 37 +------ src/syntax/validate.rs | 52 +--------- src/type_infer/convert.rs | 129 ++++++++++------------- src/type_infer/finalize.rs | 16 +-- src/type_infer/solve.rs | 73 ------------- 19 files changed, 399 insertions(+), 550 deletions(-) diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index cfcd47d..a92a90c 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -374,43 +374,6 @@ impl Backend { } } - Expression::Primitive(_, ret_type, prim, mut vals) => { - let mut values = vec![]; - let mut first_type = None; - - for val in vals.drain(..) { - let (compiled, compiled_type) = - self.compile_value_or_ref(val, variables, builder)?; - - if let Some(leftmost_type) = first_type { - assert_eq!(leftmost_type, compiled_type); - } else { - first_type = Some(compiled_type); - } - - values.push(compiled); - } - - let first_type = first_type.expect("primitive op has at least one argument"); - - // then we just need to tell Cranelift how to do each of our primitives! Much - // like Statements, above, we probably want to eventually shuffle this off into - // a separate function (maybe something off `Primitive`), but for now it's simple - // enough that we just do the `match` here. - match prim { - Primitive::Plus => Ok((builder.ins().iadd(values[0], values[1]), first_type)), - Primitive::Minus if values.len() == 2 => { - Ok((builder.ins().isub(values[0], values[1]), first_type)) - } - Primitive::Minus => Ok((builder.ins().ineg(values[0]), first_type)), - Primitive::Times => Ok((builder.ins().imul(values[0], values[1]), first_type)), - Primitive::Divide if ret_type.is_signed() => { - Ok((builder.ins().sdiv(values[0], values[1]), first_type)) - } - Primitive::Divide => Ok((builder.ins().udiv(values[0], values[1]), first_type)), - } - } - Expression::Construct(_, ty, _, fields) => { let Type::Structure(type_fields) = ty else { panic!("Got to backend with non-structure type in structure construction?!"); @@ -493,60 +456,6 @@ impl Backend { } }, - Expression::Print(_, var) => { - // Get the output buffer (or null) from our general compilation context. - let buffer_ptr = self.output_buffer_ptr(); - let buffer_ptr = builder.ins().iconst(types::I64, buffer_ptr as i64); - - // Get a reference to the string we want to print. - let var_name = match var { - ValueOrRef::Ref(_, _, ref name) => name.as_ref(), - ValueOrRef::Value(_, _, _) => "", - }; - let string_data_id = self.string_reference(var_name)?; - let local_name_ref = self - .module - .declare_data_in_func(string_data_id, builder.func); - let name_ptr = builder.ins().symbol_value(types::I64, local_name_ref); - - let var_type = var.type_of(); - let (val, _) = self.compile_value_or_ref(var, variables, builder)?; - - let (repr_val, casted_val) = match var_type { - Type::Structure(_) => (ConstantType::I64 as i64, val), - Type::Function(_, _) => (ConstantType::I64 as i64, val), - Type::Primitive(pt) => { - let constant_type = pt.into(); - - let new_val = match constant_type { - ConstantType::U64 | ConstantType::I64 | ConstantType::Void => val, - ConstantType::I8 | ConstantType::I16 | ConstantType::I32 => { - builder.ins().sextend(types::I64, val) - } - ConstantType::U8 | ConstantType::U16 | ConstantType::U32 => { - builder.ins().uextend(types::I64, val) - } - }; - - (constant_type as i64, new_val) - } - }; - - let vtype_repr = builder.ins().iconst(types::I64, repr_val); - - // Finally, we can generate the call to print. - let print_func_ref = self.runtime_functions.include_runtime_function( - "print", - &mut self.module, - builder.func, - )?; - builder.ins().call( - print_func_ref, - &[buffer_ptr, name_ptr, vtype_repr, casted_val], - ); - Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE)) - } - Expression::Bind(_, name, _, expr) => { let (value, value_type) = self.compile_expression(*expr, variables, builder)?; let variable = self.generate_local(); @@ -576,8 +485,15 @@ impl Backend { Ok((value, value_type)) } - Expression::Call(_, _, function, args) => { - let (arguments, _argument_types): (Vec<_>, Vec<_>) = args + Expression::Call(_, final_type, function, args) => { + // Get a reference to the string we want to print. + let var_name = match args[0] { + ValueOrRef::Ref(_, _, ref name) => name.to_string(), + ValueOrRef::Value(_, _, _) => "".to_string(), + ValueOrRef::Primitive(_, _, n) => format!("", n), + }; + let var_type = args[0].type_of(); + let (arguments, argument_types): (Vec<_>, Vec<_>) = args .into_iter() .map(|x| self.compile_value_or_ref(x, variables, builder)) .collect::, BackendError>>()? @@ -608,6 +524,109 @@ impl Backend { } } } + + ValueOrRef::Primitive(_, _, prim) => match prim { + Primitive::Plus => { + assert_eq!(2, arguments.len()); + Ok(( + builder.ins().iadd(arguments[0], arguments[1]), + argument_types[0], + )) + } + + Primitive::Minus => { + assert_eq!(2, arguments.len()); + Ok(( + builder.ins().isub(arguments[0], arguments[1]), + argument_types[0], + )) + } + + Primitive::Times => { + assert_eq!(2, arguments.len()); + Ok(( + builder.ins().imul(arguments[0], arguments[1]), + argument_types[0], + )) + } + + Primitive::Divide if final_type.is_signed() => { + assert_eq!(2, arguments.len()); + Ok(( + builder.ins().sdiv(arguments[0], arguments[1]), + argument_types[0], + )) + } + + Primitive::Divide => { + assert_eq!(2, arguments.len()); + Ok(( + builder.ins().udiv(arguments[0], arguments[1]), + argument_types[0], + )) + } + + Primitive::Negate => { + assert_eq!(1, arguments.len()); + Ok(( + builder.ins().ineg(arguments[0]), + argument_types[0], + )) + } + + Primitive::Print => { + // Get the output buffer (or null) from our general compilation context. + let buffer_ptr = self.output_buffer_ptr(); + let buffer_ptr = builder.ins().iconst(types::I64, buffer_ptr as i64); + + assert_eq!(1, arguments.len()); + let string_data_id = self.string_reference(&var_name)?; + let local_name_ref = self + .module + .declare_data_in_func(string_data_id, builder.func); + let name_ptr = builder.ins().symbol_value(types::I64, local_name_ref); + + let (repr_val, casted_val) = match var_type { + Type::Structure(_) => (ConstantType::I64 as i64, arguments[0]), + Type::Function(_, _) => (ConstantType::I64 as i64, arguments[0]), + Type::Primitive(pt) => { + let constant_type = pt.into(); + + let new_val = match constant_type { + ConstantType::U64 + | ConstantType::I64 + | ConstantType::Void => arguments[0], + ConstantType::I8 + | ConstantType::I16 + | ConstantType::I32 => { + builder.ins().sextend(types::I64, arguments[0]) + } + ConstantType::U8 + | ConstantType::U16 + | ConstantType::U32 => { + builder.ins().uextend(types::I64, arguments[0]) + } + }; + + (constant_type as i64, new_val) + } + }; + + let vtype_repr = builder.ins().iconst(types::I64, repr_val); + + // Finally, we can generate the call to print. + let print_func_ref = self.runtime_functions.include_runtime_function( + "print", + &mut self.module, + builder.func, + )?; + builder.ins().call( + print_func_ref, + &[buffer_ptr, name_ptr, vtype_repr, casted_val], + ); + Ok((builder.ins().iconst(types::I64, 0), VOID_REPR_TYPE)) + } + }, } } } @@ -668,6 +687,9 @@ impl Backend { Ok((value, *ctype)) } }, + ValueOrRef::Primitive(_, _, _) => { + unimplemented!() + } } } } diff --git a/src/eval/primop.rs b/src/eval/primop.rs index aeecb57..a122980 100644 --- a/src/eval/primop.rs +++ b/src/eval/primop.rs @@ -87,7 +87,7 @@ macro_rules! run_op { impl Value { fn unary_op(operation: &str, value: &Value) -> Result, PrimOpError> { match operation { - "-" => match value { + "negate" => match value { Value::I8(x) => Ok(Value::I8(x.wrapping_neg())), Value::I16(x) => Ok(Value::I16(x.wrapping_neg())), Value::I32(x) => Ok(Value::I32(x.wrapping_neg())), @@ -192,9 +192,10 @@ impl Value { right.clone(), )), }, - Value::Closure(_, _, _, _) | Value::Structure(_, _) | Value::Void => { - Err(PrimOpError::BadTypeFor(operation.to_string(), left.clone())) - } + Value::Closure(_, _, _, _) + | Value::Structure(_, _) + | Value::Primitive(_) + | Value::Void => Err(PrimOpError::BadTypeFor(operation.to_string(), left.clone())), } } diff --git a/src/eval/primtype.rs b/src/eval/primtype.rs index 0d9abc6..19a8dd4 100644 --- a/src/eval/primtype.rs +++ b/src/eval/primtype.rs @@ -42,12 +42,17 @@ impl Display for PrimitiveType { } } +#[allow(clippy::enum_variant_names)] #[derive(Clone, Debug, PartialEq, thiserror::Error)] pub enum ValuePrimitiveTypeError { #[error("Could not convert function value to primitive type (possible function name: {0:?}")] CannotConvertFunction(Option), #[error("Could not convert structure value to primitive type (possible function name: {0:?}")] CannotConvertStructure(Option), + #[error( + "Could not convert primitive operator to primitive type (possible function name: {0:?}" + )] + CannotConvertPrimitive(String), } impl<'a, IR> TryFrom<&'a Value> for PrimitiveType { @@ -72,6 +77,9 @@ impl<'a, IR> TryFrom<&'a Value> for PrimitiveType { Value::Structure(name, _) => Err(ValuePrimitiveTypeError::CannotConvertStructure( name.as_ref().map(|x| (**x).clone()), )), + Value::Primitive(prim) => Err(ValuePrimitiveTypeError::CannotConvertPrimitive( + prim.clone(), + )), } } } diff --git a/src/eval/value.rs b/src/eval/value.rs index da54910..99e8248 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -31,6 +31,7 @@ pub enum Value { Option>, HashMap, Value>, ), + Primitive(String), } impl Value { @@ -59,8 +60,13 @@ impl Value { name.clone(), fields.iter().map(|(n, v)| (n.clone(), v.strip())).collect(), ), + Value::Primitive(name) => Value::Primitive(name.clone()), } } + + pub fn primitive(name: S) -> Self { + Value::Primitive(name.to_string()) + } } fn format_value(value: &Value, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -87,6 +93,7 @@ fn format_value(value: &Value, f: &mut fmt::Formatter<'_>) -> fmt::Resul } write!(f, " }}") } + Value::Primitive(n) => write!(f, "{}", n), } } @@ -165,6 +172,10 @@ impl PartialEq> for Value { } _ => false, }, + Value::Primitive(n1) => match other { + Value::Primitive(n2) => n1 == n2, + _ => false, + }, } } } diff --git a/src/ir/arbitrary.rs b/src/ir/arbitrary.rs index b704eaa..072afc2 100644 --- a/src/ir/arbitrary.rs +++ b/src/ir/arbitrary.rs @@ -347,12 +347,25 @@ fn generate_random_expression( Some((operator, arg_count)) => { let primop = Primitive::from_str(operator).expect("chose valid primitive"); let mut args = vec![base_expr]; + let mut argtys = vec![]; while args.len() < *arg_count { args.push(generate_random_valueref(rng, env, Some(primty))); + argtys.push(Type::Primitive(primty)); } - Expression::Primitive(Location::manufactured(), out_type, primop, args) + let primtype = Type::Function(argtys, Box::new(Type::Primitive(primty))); + + Expression::Call( + Location::manufactured(), + out_type, + Box::new(ValueOrRef::Primitive( + Location::manufactured(), + primtype, + primop, + )), + args, + ) } }, Type::Structure(_) => unimplemented!(), @@ -377,9 +390,19 @@ fn generate_random_expression( } else { let (variable, var_type) = possible_variables.choose(rng).unwrap(); - Expression::Print( + Expression::Call( Location::manufactured(), - ValueOrRef::Ref(Location::manufactured(), var_type.clone(), variable.clone()), + Type::void(), + Box::new(ValueOrRef::Primitive( + Location::manufactured(), + Type::void(), + Primitive::Print, + )), + vec![ValueOrRef::Ref( + Location::manufactured(), + var_type.clone(), + variable.clone(), + )], ) } } diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 0e58076..24ab128 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -106,7 +106,6 @@ impl TopLevel { pub enum Expression { Atomic(ValueOrRef), Cast(Location, Type, ValueOrRef), - Primitive(Location, Type, Primitive, Vec>), Construct( Location, Type, @@ -115,7 +114,6 @@ pub enum Expression { ), FieldRef(Location, Type, Type, ValueOrRef, ArcIntern), Block(Location, Type, Vec>), - Print(Location, ValueOrRef), Call(Location, Type, Box>, Vec>), Bind(Location, Variable, Type, Box>), } @@ -127,11 +125,9 @@ impl Expression { match self { Expression::Atomic(x) => x.type_of(), Expression::Cast(_, t, _) => t.clone(), - Expression::Primitive(_, t, _, _) => t.clone(), Expression::Construct(_, t, _, _) => t.clone(), Expression::FieldRef(_, t, _, _, _) => t.clone(), Expression::Block(_, t, _) => t.clone(), - Expression::Print(_, _) => Type::void(), Expression::Call(_, t, _, _) => t.clone(), Expression::Bind(_, _, _, _) => Type::void(), } @@ -142,12 +138,11 @@ impl Expression { match self { Expression::Atomic(ValueOrRef::Ref(l, _, _)) => l, Expression::Atomic(ValueOrRef::Value(l, _, _)) => l, + Expression::Atomic(ValueOrRef::Primitive(l, _, _)) => l, Expression::Cast(l, _, _) => l, - Expression::Primitive(l, _, _, _) => l, Expression::Construct(l, _, _, _) => l, Expression::FieldRef(l, _, _, _, _) => l, Expression::Block(l, _, _) => l, - Expression::Print(l, _) => l, Expression::Call(l, _, _, _) => l, Expression::Bind(l, _, _, _) => l, } @@ -166,6 +161,8 @@ pub enum Primitive { Minus, Times, Divide, + Print, + Negate, } impl FromStr for Primitive { @@ -177,6 +174,8 @@ impl FromStr for Primitive { "-" => Ok(Primitive::Minus), "*" => Ok(Primitive::Times), "/" => Ok(Primitive::Divide), + "print" => Ok(Primitive::Print), + "negate" => Ok(Primitive::Negate), _ => Err(format!("Illegal primitive {}", value)), } } @@ -191,6 +190,7 @@ impl FromStr for Primitive { pub enum ValueOrRef { Value(Location, Type, Value), Ref(Location, Type, ArcIntern), + Primitive(Location, Type, Primitive), } impl ValueOrRef { @@ -198,6 +198,7 @@ impl ValueOrRef { match self { ValueOrRef::Ref(_, t, _) => t.clone(), ValueOrRef::Value(_, t, _) => t.clone(), + ValueOrRef::Primitive(_, t, _) => t.clone(), } } } diff --git a/src/ir/eval.rs b/src/ir/eval.rs index 63748b0..d74db44 100644 --- a/src/ir/eval.rs +++ b/src/ir/eval.rs @@ -1,4 +1,4 @@ -use super::{Primitive, Type, ValueOrRef}; +use super::{Type, ValueOrRef}; use crate::eval::{EvalError, Value}; use crate::ir::{Expression, Program, TopLevel, Variable}; use crate::util::scoped_map::ScopedMap; @@ -65,22 +65,6 @@ where } } - Expression::Primitive(_, _, op, args) => { - let arg_values = args - .iter() - .map(|x| x.eval(env)) - .collect::>, IREvalError>>()?; - - // and then finally we call `calculate` to run them. trust me, it's nice - // to not have to deal with all the nonsense hidden under `calculate`. - match op { - Primitive::Plus => Ok(Value::calculate("+", arg_values)?), - Primitive::Minus => Ok(Value::calculate("-", arg_values)?), - Primitive::Times => Ok(Value::calculate("*", arg_values)?), - Primitive::Divide => Ok(Value::calculate("/", arg_values)?), - } - } - Expression::Construct(_, _, name, fields) => { let mut result_fields = HashMap::with_capacity(fields.len()); @@ -114,16 +98,6 @@ where Ok(result) } - Expression::Print(_, value) => { - let n = match value { - ValueOrRef::Ref(_, _, ref name) => name.as_str(), - ValueOrRef::Value(_, _, _) => "", - }; - let value = value.eval(env)?; - stdout.push_str(&format!("{} = {}\n", n, value)); - Ok(Value::Void) - } - Expression::Bind(_, name, _, value) => { let value = value.eval(env, stdout)?; env.insert(name.clone(), value.clone()); @@ -153,6 +127,28 @@ where closure_env.release_scope(); Ok(result) } + + Value::Primitive(name) if name == "print" => { + if let [ValueOrRef::Ref(loc, ty, name)] = &args[..] { + let value = ValueOrRef::Ref(loc.clone(), ty.clone(), name.clone()).eval(env)?; + let addendum = format!("{} = {}\n", name, value); + + stdout.push_str(&addendum); + Ok(Value::Void) + } else { + panic!("Non-reference/non-singleton argument to 'print'"); + } + } + + Value::Primitive(name) => { + let values = args + .iter() + .map(|x| x.eval(env)) + .collect::>()?; + println!("primitive {}: args {:?}", name, values); + Value::calculate(name.as_str(), values).map_err(Into::into) + } + _ => Err(EvalError::NotAFunction(loc.clone(), function)), } } @@ -179,6 +175,8 @@ impl ValueOrRef { .get(n) .cloned() .ok_or_else(|| EvalError::LookupFailed(loc.clone(), n.to_string())), + + ValueOrRef::Primitive(_, _, prim) => Ok(Value::primitive(prim)), } } } diff --git a/src/ir/pretty.rs b/src/ir/pretty.rs index 129d8f9..ab55bc3 100644 --- a/src/ir/pretty.rs +++ b/src/ir/pretty.rs @@ -53,9 +53,6 @@ impl Expression { .append(t.pretty(allocator)) .append(allocator.text(">")) .append(e.pretty(allocator)), - Expression::Primitive(_, _, op, exprs) if exprs.len() == 1 => { - op.pretty(allocator).append(exprs[0].pretty(allocator)) - } Expression::Construct(_, _, name, fields) => { let inner = allocator .intersperse( @@ -78,19 +75,6 @@ impl Expression { .text(".") .append(allocator.text(field.to_string())), ), - Expression::Primitive(_, _, op, exprs) if exprs.len() == 2 => { - let left = exprs[0].pretty(allocator); - let right = exprs[1].pretty(allocator); - - left.append(allocator.space()) - .append(op.pretty(allocator)) - .append(allocator.space()) - .append(right) - .parens() - } - Expression::Primitive(_, _, op, exprs) => { - allocator.text(format!("!!{:?} with {} arguments!!", op, exprs.len())) - } Expression::Call(_, _, fun, args) => { let args = args.iter().map(|x| x.pretty(allocator)); let comma_sepped_args = allocator.intersperse(args, allocator.text(",")); @@ -119,10 +103,6 @@ impl Expression { result.append(last).append(allocator.text("}")) } }, - Expression::Print(_, var) => allocator - .text("print") - .append(allocator.space()) - .append(var.pretty(allocator)), Expression::Bind(_, var, ty, expr) => allocator .text(var.as_ref().to_string()) .append(allocator.space()) @@ -144,6 +124,8 @@ impl Primitive { Primitive::Minus => allocator.text("-"), Primitive::Times => allocator.text("*"), Primitive::Divide => allocator.text("/"), + Primitive::Print => allocator.text("print"), + Primitive::Negate => allocator.text("negate"), } } } @@ -153,6 +135,7 @@ impl ValueOrRef { match self { ValueOrRef::Value(_, _, v) => v.pretty(allocator), ValueOrRef::Ref(_, _, v) => allocator.text(v.as_ref().to_string()), + ValueOrRef::Primitive(_, _, p) => p.pretty(allocator), } } } diff --git a/src/repl.rs b/src/repl.rs index 58225f7..d2730ef 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,5 +1,5 @@ use crate::backend::{Backend, BackendError}; -use crate::syntax::{ConstantType, Expression, Location, ParserError, Statement, TopLevel}; +use crate::syntax::{ConstantType, Expression, Location, ParserError, TopLevel}; use crate::type_infer::TypeInferenceResult; use crate::util::scoped_map::ScopedMap; use codespan_reporting::diagnostic::Diagnostic; @@ -130,7 +130,7 @@ impl REPL { let syntax = TopLevel::parse(entry, source)?; let program = match syntax { - TopLevel::Statement(Statement::Expression(Expression::Binding(loc, name, expr))) => { + TopLevel::Expression(Expression::Binding(loc, name, expr)) => { // if this is a variable binding, and we've never defined this variable before, // we should tell cranelift about it. this is optimistic; if we fail to compile, // then we won't use this definition until someone tries again. @@ -142,12 +142,15 @@ impl REPL { crate::syntax::Program { items: vec![ - TopLevel::Statement(Statement::Expression(Expression::Binding( + TopLevel::Expression(Expression::Binding(loc.clone(), name.clone(), expr)), + TopLevel::Expression(Expression::Call( loc.clone(), - name.clone(), - expr, - ))), - TopLevel::Statement(Statement::Print(loc, name)), + Box::new(Expression::Primitive( + loc.clone(), + crate::syntax::Name::manufactured("print"), + )), + vec![Expression::Reference(loc.clone(), name.name)], + )), ], } } diff --git a/src/syntax.rs b/src/syntax.rs index 751c36d..26d0b36 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -285,35 +285,39 @@ fn order_of_operations() { assert_eq!( Program::from_str(muladd1).unwrap(), Program { - items: vec![TopLevel::Statement(Statement::Expression( - Expression::Binding( - Location::new(testfile, 0..1), - Name::manufactured("x"), + items: vec![TopLevel::Expression(Expression::Binding( + Location::new(testfile, 0..1), + Name::manufactured("x"), + Box::new(Expression::Call( + Location::new(testfile, 6..7), Box::new(Expression::Primitive( Location::new(testfile, 6..7), - "+".to_string(), - vec![ - Expression::Value( - Location::new(testfile, 4..5), - Value::Number(None, None, 1), - ), - Expression::Primitive( + Name::manufactured("+") + )), + vec![ + Expression::Value( + Location::new(testfile, 4..5), + Value::Number(None, None, 1), + ), + Expression::Call( + Location::new(testfile, 10..11), + Box::new(Expression::Primitive( Location::new(testfile, 10..11), - "*".to_string(), - vec![ - Expression::Value( - Location::new(testfile, 8..9), - Value::Number(None, None, 2), - ), - Expression::Value( - Location::new(testfile, 12..13), - Value::Number(None, None, 3), - ), - ] - ) - ] - )) - ) + Name::manufactured("*") + )), + vec![ + Expression::Value( + Location::new(testfile, 8..9), + Value::Number(None, None, 2), + ), + Expression::Value( + Location::new(testfile, 12..13), + Value::Number(None, None, 3), + ), + ] + ) + ] + )) ))], } ); diff --git a/src/syntax/arbitrary.rs b/src/syntax/arbitrary.rs index 9abfa1f..44bd323 100644 --- a/src/syntax/arbitrary.rs +++ b/src/syntax/arbitrary.rs @@ -1,4 +1,4 @@ -use crate::syntax::ast::{ConstantType, Expression, Name, Program, Statement, TopLevel, Value}; +use crate::syntax::ast::{ConstantType, Expression, Name, Program, TopLevel, Value}; use crate::syntax::location::Location; use proptest::sample::select; use proptest::{ @@ -72,19 +72,26 @@ impl Arbitrary for Program { genenv.bindings.insert(psi.name.clone(), psi.binding_type); items.push( expr.prop_map(move |expr| { - TopLevel::Statement(Statement::Expression(Expression::Binding( + TopLevel::Expression(Expression::Binding( Location::manufactured(), psi.name.clone(), Box::new(expr), - ))) + )) }) .boxed(), ); } else { let printers = genenv.bindings.keys().map(|n| { - Just(TopLevel::Statement(Statement::Print( + Just(TopLevel::Expression(Expression::Call( Location::manufactured(), - Name::manufactured(n), + Box::new(Expression::Primitive( + Location::manufactured(), + Name::manufactured("print"), + )), + vec![Expression::Reference( + Location::manufactured(), + n.to_string(), + )], ))) }); items.push(Union::new(printers).boxed()); @@ -186,7 +193,14 @@ impl Arbitrary for Expression { while args.len() > count { args.pop(); } - Expression::Primitive(Location::manufactured(), oper.to_string(), args) + Expression::Call( + Location::manufactured(), + Box::new(Expression::Primitive( + Location::manufactured(), + Name::manufactured(oper), + )), + args, + ) }) }) .boxed() diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index f128ecb..726ad5a 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -26,7 +26,7 @@ pub struct Program { /// and functions #[derive(Clone, Debug, PartialEq)] pub enum TopLevel { - Statement(Statement), + Expression(Expression), Function( Option, Vec<(Name, Option)>, @@ -86,38 +86,6 @@ impl fmt::Display for Name { } } -/// 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 { - Print(Location, Name), - Expression(Expression), -} - -impl PartialEq for Statement { - fn eq(&self, other: &Self) -> bool { - match self { - Statement::Print(_, name1) => match other { - Statement::Print(_, name2) => name1 == name2, - _ => false, - }, - Statement::Expression(e1) => match other { - Statement::Expression(e2) => e1 == e2, - _ => false, - }, - } - } -} - /// An expression in the underlying syntax. /// /// Like statements, these expressions are guaranteed to have been @@ -131,12 +99,25 @@ pub enum Expression { Reference(Location, String), FieldRef(Location, Box, Name), Cast(Location, String, Box), - Primitive(Location, String, Vec), + Primitive(Location, Name), Call(Location, Box, Vec), - Block(Location, Vec), + Block(Location, Vec), Binding(Location, Name, Box), } +impl Expression { + pub fn primitive(loc: Location, name: &str, args: Vec) -> Expression { + Expression::Call( + loc.clone(), + Box::new(Expression::Primitive( + loc.clone(), + Name::new(name, loc.clone()), + )), + args, + ) + } +} + impl PartialEq for Expression { fn eq(&self, other: &Self) -> bool { match self { @@ -160,8 +141,8 @@ impl PartialEq for Expression { Expression::Cast(_, t2, e2) => t1 == t2 && e1 == e2, _ => false, }, - Expression::Primitive(_, prim1, args1) => match other { - Expression::Primitive(_, prim2, args2) => prim1 == prim2 && args1 == args2, + Expression::Primitive(_, prim1) => match other { + Expression::Primitive(_, prim2) => prim1 == prim2, _ => false, }, Expression::Call(_, f1, a1) => match other { @@ -189,7 +170,7 @@ impl Expression { Expression::Reference(loc, _) => loc, Expression::FieldRef(loc, _, _) => loc, Expression::Cast(loc, _, _) => loc, - Expression::Primitive(loc, _, _) => loc, + Expression::Primitive(loc, _) => loc, Expression::Call(loc, _, _) => loc, Expression::Block(loc, _) => loc, Expression::Binding(loc, _, _) => loc, diff --git a/src/syntax/eval.rs b/src/syntax/eval.rs index 7546392..f4635f7 100644 --- a/src/syntax/eval.rs +++ b/src/syntax/eval.rs @@ -1,5 +1,5 @@ use crate::eval::{EvalError, PrimitiveType, Value}; -use crate::syntax::{ConstantType, Expression, Name, Program, Statement, TopLevel}; +use crate::syntax::{ConstantType, Expression, Name, Program, TopLevel}; use crate::util::scoped_map::ScopedMap; use internment::ArcIntern; use std::collections::HashMap; @@ -40,7 +40,7 @@ impl Program { } } - TopLevel::Statement(stmt) => last_result = stmt.eval(&mut stdout, &mut env)?, + TopLevel::Expression(expr) => last_result = expr.eval(&mut stdout, &mut env)?, TopLevel::Structure(_, _, _) => { last_result = Value::Void; @@ -52,32 +52,6 @@ impl Program { } } -impl Statement { - fn eval( - &self, - stdout: &mut String, - env: &mut ScopedMap, Value>, - ) -> Result, EvalError> { - match self { - Statement::Print(loc, name) => { - let value = env - .get(&name.clone().intern()) - .ok_or_else(|| EvalError::LookupFailed(loc.clone(), name.name.clone()))?; - let value = if let Value::Number(x) = value { - Value::U64(*x) - } else { - value.clone() - }; - let line = format!("{} = {}\n", name, value); - stdout.push_str(&line); - Ok(Value::Void) - } - - Statement::Expression(e) => e.eval(stdout, env), - } - } -} - impl Expression { fn eval( &self, @@ -144,16 +118,7 @@ impl Expression { Ok(target_type.safe_cast(&value)?) } - Expression::Primitive(_, op, args) => { - let mut arg_values = Vec::with_capacity(args.len()); - - for arg in args.iter() { - // yay, recursion! makes this pretty straightforward - arg_values.push(arg.eval(stdout, env)?); - } - - Ok(Value::calculate(op, arg_values)?) - } + Expression::Primitive(_, op) => Ok(Value::primitive(op.name.clone())), Expression::Call(loc, fun, args) => { let function = fun.eval(stdout, env)?; @@ -178,6 +143,31 @@ impl Expression { closure_env.release_scope(); Ok(result) } + + Value::Primitive(name) if name == "print" => { + if let [Expression::Reference(_, name)] = &args[..] { + let value = Expression::Reference(loc.clone(), name.clone()).eval(stdout, env)?; + let value = match value { + Value::Number(x) => Value::U64(x), + x => x, + }; + let addendum = format!("{} = {}\n", name, value); + + stdout.push_str(&addendum); + Ok(Value::Void) + } else { + panic!("Non-reference/non-singleton argument to 'print': {:?}", args); + } + } + + Value::Primitive(name) => { + let values = args + .iter() + .map(|x| x.eval(stdout, env)) + .collect::>()?; + Value::calculate(name.as_str(), values).map_err(Into::into) + } + _ => Err(EvalError::NotAFunction(loc.clone(), function)), } } diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index 3c37a92..ed42ca4 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -9,7 +9,7 @@ //! eventually want to leave lalrpop behind.) //! use crate::syntax::{Location, ParserError}; -use crate::syntax::ast::{Program,TopLevel,Statement,Expression,Value,Name,Type}; +use crate::syntax::ast::{Program,TopLevel,Expression,Value,Name,Type}; use crate::syntax::tokens::{ConstantType, Token}; use internment::ArcIntern; @@ -81,7 +81,7 @@ ProgramTopLevel: Vec = { pub TopLevel: TopLevel = { => f, => s, - ";" => TopLevel::Statement(s), + ";" => TopLevel::Expression(s), } Function: TopLevel = { @@ -132,34 +132,6 @@ TypeName: Name = { Name::new(v, Location::new(file_idx, name_start..name_end)), } -Statements: Vec = { - // a statement is either a set of statements followed by another - // statement (note, here, that you can name the result of a sub-parse - // using ) ... - ";" => { - stmts.push(stmt); - stmts - }, - - => { - vec![stmt] - } -} - -#[inline] -Statement: Statement = { - // A statement can just be a print statement. - "print" "> => - Statement::Print( - Location::new(file_idx, ls..le), - Name::new(v, Location::new(file_idx, name_start..name_end)), - ), - - // A statement can just be an expression. - => - Statement::Expression(e), -} - // Expressions! Expressions are a little fiddly, because we're going to // use a little bit of a trick to make sure that we get operator precedence // right. The trick works by creating a top-level `Expression` grammar entry @@ -198,6 +170,21 @@ BindingExpression: Expression = { Box::new(e), ), + PrintExpression, +} + +PrintExpression: Expression = { + "print" => + Expression::Call( + Location::new(file_idx, ls..le), + Box::new( + Expression::Primitive( + Location::new(file_idx, ls..pe), + Name::new("print", Location::new(file_idx, ls..pe)), + ), + ), + vec![e], + ), ConstructorExpression, } @@ -214,24 +201,24 @@ FieldSetter: (Name, Expression) = { // we group addition and subtraction under the heading "additive" AdditiveExpression: Expression = { "+" => - Expression::Primitive(Location::new(file_idx, ls..le), "+".to_string(), vec![e1, e2]), + Expression::primitive(Location::new(file_idx, ls..le), "+", vec![e1, e2]), "-" => - Expression::Primitive(Location::new(file_idx, ls..le), "-".to_string(), vec![e1, e2]), + Expression::primitive(Location::new(file_idx, ls..le), "-", vec![e1, e2]), MultiplicativeExpression, } // similarly, we group multiplication and division under "multiplicative" MultiplicativeExpression: Expression = { "*" => - Expression::Primitive(Location::new(file_idx, ls..le), "*".to_string(), vec![e1, e2]), + Expression::primitive(Location::new(file_idx, ls..le), "*", vec![e1, e2]), "/" => - Expression::Primitive(Location::new(file_idx, ls..le), "/".to_string(), vec![e1, e2]), + Expression::primitive(Location::new(file_idx, ls..le), "/", vec![e1, e2]), UnaryExpression, } UnaryExpression: Expression = { "-" => - Expression::Primitive(Location::new(file_idx, l..le), "-".to_string(), vec![e]), + Expression::primitive(Location::new(file_idx, l..le), "negate", vec![e]), "<" "> ">" => Expression::Cast(Location::new(file_idx, l..le), v.to_string(), Box::new(e)), CallExpression, @@ -257,13 +244,21 @@ AtomicExpression: Expression = { // just a number "> => Expression::Value(Location::new(file_idx, l..end), Value::Number(n.0, n.1, n.2)), // this expression could actually be a block! - "{" "}" => Expression::Block(Location::new(file_idx, s..e), stmts), + "{" ";"? "}" => Expression::Block(Location::new(file_idx, s..e), exprs), "{" "}" => Expression::Block(Location::new(file_idx, s..e), vec![]), // finally, let people parenthesize expressions and get back to a // lower precedence "(" ")" => e, } +Expressions: Vec = { + => vec![e], + ";" => { + exps.push(e); + exps + } +} + // Lifted from the LALRPop book, a comma-separated list of T that may or // may not conclude with a comma. Comma: Vec = { diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index 249c571..81eabe2 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -1,4 +1,4 @@ -use crate::syntax::ast::{ConstantType, Expression, Program, Statement, TopLevel, Type, Value}; +use crate::syntax::ast::{ConstantType, Expression, Program, TopLevel, Type, Value}; use crate::util::pretty::{derived_display, Allocator}; use pretty::{DocAllocator, DocBuilder}; @@ -20,7 +20,7 @@ impl Program { impl TopLevel { pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { match self { - TopLevel::Statement(stmt) => stmt.pretty(allocator), + TopLevel::Expression(expr) => expr.pretty(allocator), TopLevel::Function(name, args, rettype, body) => allocator .text("function") .append(allocator.space()) @@ -87,18 +87,6 @@ impl TopLevel { } } -impl Statement { - pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { - match self { - Statement::Print(_, var) => allocator - .text("print") - .append(allocator.space()) - .append(allocator.text(var.to_string())), - Statement::Expression(e) => e.pretty(allocator), - } - } -} - impl Expression { pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { match self { @@ -129,25 +117,7 @@ impl Expression { .text(t.clone()) .angles() .append(e.pretty(allocator)), - Expression::Primitive(_, op, exprs) if exprs.len() == 1 => allocator - .text(op.to_string()) - .append(exprs[0].pretty(allocator)), - Expression::Primitive(_, op, exprs) if exprs.len() == 2 => { - let left = exprs[0].pretty(allocator); - let right = exprs[1].pretty(allocator); - - left.append(allocator.space()) - .append(allocator.text(op.to_string())) - .append(allocator.space()) - .append(right) - .parens() - } - Expression::Primitive(_, op, exprs) => { - let call = allocator.text(op.to_string()); - let args = exprs.iter().map(|x| x.pretty(allocator)); - let comma_sepped_args = allocator.intersperse(args, allocator.text(",")); - call.append(comma_sepped_args.parens()) - } + Expression::Primitive(_, op) => allocator.text(op.name.clone()), Expression::Call(_, fun, args) => { let args = args.iter().map(|x| x.pretty(allocator)); let comma_sepped_args = allocator.intersperse(args, allocator.text(",")); @@ -245,6 +215,5 @@ impl Type { derived_display!(Program); derived_display!(TopLevel); -derived_display!(Statement); derived_display!(Expression); derived_display!(Value); diff --git a/src/syntax/validate.rs b/src/syntax/validate.rs index 3c868d0..8ee2792 100644 --- a/src/syntax/validate.rs +++ b/src/syntax/validate.rs @@ -1,6 +1,6 @@ use crate::{ eval::PrimitiveType, - syntax::{Expression, Location, Program, Statement, TopLevel}, + syntax::{Expression, Location, Program, TopLevel}, util::scoped_map::ScopedMap, }; use codespan_reporting::diagnostic::Diagnostic; @@ -132,47 +132,12 @@ impl TopLevel { bound_variables.release_scope(); result } - TopLevel::Statement(stmt) => stmt.validate(bound_variables), + TopLevel::Expression(expr) => expr.validate(bound_variables), TopLevel::Structure(_, _, _) => (vec![], vec![]), } } } -impl Statement { - /// Validate that the statement makes semantic sense, not just syntactic sense. - /// - /// This checks for things like references to variables that don't exist, for - /// example, and generates warnings for things that are inadvisable but not - /// actually a problem. Since statements appear in a broader context, you'll - /// need to provide the set of variables that are bound where this statement - /// occurs. We use a `HashMap` to map these bound locations to the locations - /// where their bound, because these locations are handy when generating errors - /// and warnings. - fn validate( - &self, - bound_variables: &mut ScopedMap, - ) -> (Vec, Vec) { - let mut errors = vec![]; - let mut warnings = vec![]; - - match self { - Statement::Print(_, var) if bound_variables.contains_key(&var.name) => {} - Statement::Print(loc, var) => { - errors.push(Error::UnboundVariable(loc.clone(), var.to_string())) - } - - Statement::Expression(e) => { - let (mut exp_errors, mut exp_warnings) = e.validate(bound_variables); - - errors.append(&mut exp_errors); - warnings.append(&mut exp_warnings); - } - } - - (errors, warnings) - } -} - impl Expression { fn validate( &self, @@ -207,18 +172,7 @@ impl Expression { (errs, warns) } - Expression::Primitive(_, _, args) => { - let mut errors = vec![]; - let mut warnings = vec![]; - - for expr in args.iter() { - let (mut err, mut warn) = expr.validate(variable_map); - errors.append(&mut err); - warnings.append(&mut warn); - } - - (errors, warnings) - } + Expression::Primitive(_, _) => (vec![], vec![]), Expression::Call(_, func, args) => { let (mut errors, mut warnings) = func.validate(variable_map); diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 1fdf95d..fb69cba 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -9,7 +9,7 @@ use std::str::FromStr; enum TopLevelItem { Type(ArcIntern, ir::TypeOrVar), - Expression(ir::TopLevel), + Value(ir::TopLevel), } /// This function takes a syntactic program and converts it into the IR version of the @@ -32,7 +32,7 @@ pub fn convert_program( let tli = convert_top_level(item, &mut constraint_db, &mut renames, &mut bindings); match tli { - TopLevelItem::Expression(item) => items.push(item), + TopLevelItem::Value(item) => items.push(item), TopLevelItem::Type(name, decl) => { let _ = type_definitions.insert(name, decl); } @@ -137,7 +137,7 @@ fn convert_top_level( // Remember to exit this scoping level! renames.release_scope(); - TopLevelItem::Expression(ir::TopLevel::Function( + TopLevelItem::Value(ir::TopLevel::Function( function_name, arginfo, rettype, @@ -145,8 +145,8 @@ fn convert_top_level( )) } - syntax::TopLevel::Statement(stmt) => TopLevelItem::Expression(ir::TopLevel::Statement( - convert_statement(stmt, constraint_db, renames, bindings), + syntax::TopLevel::Expression(expr) => TopLevelItem::Value(ir::TopLevel::Statement( + convert_expression(expr, constraint_db, renames, bindings).0, )), syntax::TopLevel::Structure(_loc, name, fields) => { @@ -161,46 +161,6 @@ fn convert_top_level( } } -/// This function takes a syntactic statements and converts it into a series of -/// IR statements, adding type variables and constraints as necessary. -/// -/// We generate a series of statements because we're going to flatten all -/// incoming expressions so that they are no longer recursive. This will -/// generate a bunch of new bindings for all the subexpressions, which we -/// return as a bundle. -/// -/// See the safety warning on [`convert_program`]! This function assumes that -/// you have run [`Statement::validate`], and will trigger panics in error -/// conditions if you have run that and had it come back clean. -fn convert_statement( - statement: syntax::Statement, - constraint_db: &mut Vec, - renames: &mut ScopedMap, ArcIntern>, - bindings: &mut HashMap, ir::TypeOrVar>, -) -> ir::Expression { - match statement { - syntax::Statement::Print(loc, name) => { - let iname = ArcIntern::new(name.to_string()); - let final_name = renames - .get(&iname) - .cloned() - .unwrap_or_else(|| iname.clone()); - let varty = bindings - .get(&final_name) - .expect("print variable defined before use") - .clone(); - - constraint_db.push(Constraint::Printable(loc.clone(), varty.clone())); - - ir::Expression::Print(loc.clone(), ir::ValueOrRef::Ref(loc, varty, final_name)) - } - - syntax::Statement::Expression(e) => { - convert_expression(e, constraint_db, renames, bindings).0 - } - } -} - /// This function takes a syntactic expression and converts it into a series /// of IR statements, adding type variables and constraints as necessary. /// @@ -359,39 +319,54 @@ fn convert_expression( (finalize_expression(prereqs, res), target_type) } - syntax::Expression::Primitive(loc, fun, mut args) => { - let primop = ir::Primitive::from_str(&fun).expect("valid primitive"); - let mut prereqs = vec![]; - let mut nargs = vec![]; - let mut atypes = vec![]; - let ret_type = ir::TypeOrVar::new(); + syntax::Expression::Primitive(loc, name) => { + let primop = ir::Primitive::from_str(&name.name).expect("valid primitive"); - for arg in args.drain(..) { - let (aexp, atype) = convert_expression(arg, constraint_db, renames, bindings); - let (aprereqs, asimple) = simplify_expr(aexp); + match primop { + ir::Primitive::Plus | ir::Primitive::Times | ir::Primitive::Divide => { + let numeric_type = ir::TypeOrVar::new_located(loc.clone()); + constraint_db.push(Constraint::NumericType(loc.clone(), numeric_type.clone())); + let funtype = ir::TypeOrVar::Function( + vec![numeric_type.clone(), numeric_type.clone()], + Box::new(numeric_type.clone()), + ); + let result_value = ir::ValueOrRef::Primitive(loc, funtype.clone(), primop); + (ir::Expression::Atomic(result_value), funtype) + } - merge_prereq(&mut prereqs, aprereqs); - nargs.push(asimple); - atypes.push(atype); - } + ir::Primitive::Minus => { + let numeric_type = ir::TypeOrVar::new_located(loc.clone()); + constraint_db.push(Constraint::NumericType(loc.clone(), numeric_type.clone())); + let funtype = ir::TypeOrVar::Function( + vec![numeric_type.clone(), numeric_type.clone()], + Box::new(numeric_type.clone()), + ); + let result_value = ir::ValueOrRef::Primitive(loc, funtype.clone(), primop); + (ir::Expression::Atomic(result_value), funtype) + } - constraint_db.push(Constraint::ProperPrimitiveArgs( - loc.clone(), - primop, - atypes.clone(), - ret_type.clone(), - )); + ir::Primitive::Print => { + let arg_type = ir::TypeOrVar::new_located(loc.clone()); + constraint_db.push(Constraint::Printable(loc.clone(), arg_type.clone())); + let funtype = ir::TypeOrVar::Function( + vec![arg_type], + Box::new(ir::TypeOrVar::Primitive(PrimitiveType::Void)), + ); + let result_value = ir::ValueOrRef::Primitive(loc, funtype.clone(), primop); + (ir::Expression::Atomic(result_value), funtype) + } - let last_call = ir::Expression::Primitive(loc.clone(), ret_type.clone(), primop, nargs); - - if prereqs.is_empty() { - (last_call, ret_type) - } else { - prereqs.push(last_call); - ( - ir::Expression::Block(loc, ret_type.clone(), prereqs), - ret_type, - ) + ir::Primitive::Negate => { + let arg_type = ir::TypeOrVar::new_located(loc.clone()); + constraint_db.push(Constraint::NumericType(loc.clone(), arg_type.clone())); + constraint_db.push(Constraint::IsSigned(loc.clone(), arg_type.clone())); + let funtype = ir::TypeOrVar::Function( + vec![arg_type.clone()], + Box::new(arg_type), + ); + let result_value = ir::ValueOrRef::Primitive(loc, funtype.clone(), primop); + (ir::Expression::Atomic(result_value), funtype) + } } } @@ -444,10 +419,10 @@ fn convert_expression( let mut ret_type = ir::TypeOrVar::Primitive(PrimitiveType::Void); let mut exprs = vec![]; - for statement in stmts { - let expr = convert_statement(statement, constraint_db, renames, bindings); + for xpr in stmts { + let (expr, expr_type) = convert_expression(xpr, constraint_db, renames, bindings); - ret_type = expr.type_of(); + ret_type = expr_type; exprs.push(expr); } diff --git a/src/type_infer/finalize.rs b/src/type_infer/finalize.rs index 8949c26..7f12adb 100644 --- a/src/type_infer/finalize.rs +++ b/src/type_infer/finalize.rs @@ -54,15 +54,6 @@ fn finalize_expression( finalize_val_or_ref(val_or_ref, resolutions), ), - Expression::Primitive(loc, ty, prim, mut args) => Expression::Primitive( - loc, - finalize_type(ty, resolutions), - prim, - args.drain(..) - .map(|x| finalize_val_or_ref(x, resolutions)) - .collect(), - ), - Expression::Construct(loc, ty, name, fields) => Expression::Construct( loc, finalize_type(ty, resolutions), @@ -97,10 +88,6 @@ fn finalize_expression( Expression::Block(loc, finalize_type(ty, resolutions), final_exprs) } - Expression::Print(loc, var) => { - Expression::Print(loc, finalize_val_or_ref(var, resolutions)) - } - Expression::Call(loc, ty, fun, args) => Expression::Call( loc, finalize_type(ty, resolutions), @@ -147,6 +134,9 @@ fn finalize_val_or_ref( ) -> ValueOrRef { match valref { ValueOrRef::Ref(loc, ty, var) => ValueOrRef::Ref(loc, finalize_type(ty, resolutions), var), + ValueOrRef::Primitive(loc, ty, prim) => { + ValueOrRef::Primitive(loc, finalize_type(ty, resolutions), prim) + } ValueOrRef::Value(loc, ty, val) => { let new_type = finalize_type(ty, resolutions); diff --git a/src/type_infer/solve.rs b/src/type_infer/solve.rs index b18d284..15dc067 100644 --- a/src/type_infer/solve.rs +++ b/src/type_infer/solve.rs @@ -12,8 +12,6 @@ pub enum Constraint { Printable(Location, TypeOrVar), /// The provided numeric value fits in the given constant type FitsInNumType(Location, TypeOrVar, u64), - /// The given primitive has the proper arguments types associated with it - ProperPrimitiveArgs(Location, Primitive, Vec, TypeOrVar), /// The given type can be casted to the target type safely CanCastTo(Location, TypeOrVar, TypeOrVar), /// The given type has the given field in it, and the type of that field @@ -41,13 +39,6 @@ impl fmt::Display for Constraint { match self { Constraint::Printable(_, ty) => write!(f, "PRINTABLE {}", ty), Constraint::FitsInNumType(_, ty, num) => write!(f, "FITS_IN {} {}", num, ty), - Constraint::ProperPrimitiveArgs(_, op, args, ret) if args.len() == 1 => { - write!(f, "PRIM {} {} -> {}", op, args[0], ret) - } - Constraint::ProperPrimitiveArgs(_, op, args, ret) if args.len() == 2 => { - write!(f, "PRIM {} ({}, {}) -> {}", op, args[0], args[1], ret) - } - Constraint::ProperPrimitiveArgs(_, op, _, ret) => write!(f, "PRIM {} -> {}", op, ret), Constraint::CanCastTo(_, ty, ty2) => write!(f, "CAST {} -> {}", ty, ty2), Constraint::TypeHasField(_, ty1, field, ty2) => { write!(f, "FIELD {}.{} -> {}", ty1, field, ty2) @@ -84,10 +75,6 @@ impl Constraint { Constraint::IsSigned(_, ty) => ty.replace(name, replace_with), Constraint::IsSomething(_, ty) => ty.replace(name, replace_with), Constraint::NumericType(_, ty) => ty.replace(name, replace_with), - Constraint::ProperPrimitiveArgs(_, _, args, ret) => { - ret.replace(name, replace_with) - | args.iter_mut().any(|x| x.replace(name, replace_with)) - } Constraint::NamedTypeIs(_, name, ty) => ty.replace(name, replace_with), } } @@ -269,12 +256,6 @@ impl From for Diagnostic { TypeInferenceError::CouldNotSolve(Constraint::Printable(loc, ty)) => loc .labelled_error("internal error") .with_message(format!("Could not determine if type {} was printable", ty)), - TypeInferenceError::CouldNotSolve(Constraint::ProperPrimitiveArgs(loc, prim, _, _)) => { - loc.labelled_error("internal error").with_message(format!( - "Could not tell if primitive {} received the proper argument types", - prim - )) - } TypeInferenceError::CouldNotSolve(Constraint::IsSomething(loc, _)) => { loc.labelled_error("could not infer type") .with_message("Could not find *any* type information; is this an unused function argument?") @@ -621,60 +602,6 @@ pub fn solve_constraints( changed_something = true; } - Constraint::ProperPrimitiveArgs(loc, prim, mut args, ret) => match prim { - Primitive::Plus | Primitive::Minus | Primitive::Times | Primitive::Divide - if args.len() == 2 => - { - let right = args.pop().expect("2>0"); - let left = args.pop().expect("2>1"); - - new_constraints.push(Constraint::NumericType(loc.clone(), left.clone())); - new_constraints.push(Constraint::Equivalent( - loc.clone(), - left.clone(), - right, - )); - new_constraints.push(Constraint::Equivalent(loc.clone(), left, ret)); - changed_something = true; - all_constraints_solved = false; - tracing::trace!(primitive = %prim, "we expanded out a binary primitive operation"); - } - - Primitive::Minus if args.len() == 1 => { - let value = args.pop().expect("1>0"); - new_constraints.push(Constraint::NumericType(loc.clone(), value.clone())); - new_constraints.push(Constraint::IsSigned(loc.clone(), value.clone())); - new_constraints.push(Constraint::Equivalent(loc, value, ret)); - changed_something = true; - all_constraints_solved = false; - tracing::trace!(primitive = %prim, "we expanded out a unary primitive operation"); - } - - Primitive::Plus | Primitive::Times | Primitive::Divide => { - errors.push(TypeInferenceError::WrongPrimitiveArity( - loc, - prim, - 2, - 2, - args.len(), - )); - changed_something = true; - tracing::trace!(primitive = %prim, provided_arity = args.len(), "binary primitive operation arity is wrong"); - } - - Primitive::Minus => { - errors.push(TypeInferenceError::WrongPrimitiveArity( - loc, - prim, - 1, - 2, - args.len(), - )); - changed_something = true; - tracing::trace!(primitive = %prim, provided_arity = args.len(), "unary primitive operation arity is wrong"); - } - }, - // Some equivalences we can/should solve directly Constraint::Equivalent( loc, -- 2.53.0 From 6800064bdf6eabadc12b05484c568a148616dd34 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Thu, 18 Apr 2024 12:33:34 -0700 Subject: [PATCH 49/59] Separate the IR evaluator, add an interesting stack-breaking case. --- build.rs | 3 +- examples/basic/generated0004.ngr | 150 +++++++++++++++++++++++++++++++ src/backend/eval.rs | 34 ++++--- src/backend/into_crane.rs | 7 +- src/bin/ngrun.rs | 3 +- src/eval/primtype.rs | 2 +- src/ir.rs | 1 + src/ir/arbitrary.rs | 12 +-- src/ir/ast.rs | 4 +- src/ir/eval.rs | 138 ++++++++++++++++------------ src/syntax.rs | 27 +----- src/syntax/arbitrary.rs | 2 +- src/syntax/eval.rs | 8 +- src/type_infer.rs | 3 +- src/type_infer/convert.rs | 9 +- src/type_infer/finalize.rs | 2 +- 16 files changed, 283 insertions(+), 122 deletions(-) create mode 100644 examples/basic/generated0004.ngr diff --git a/build.rs b/build.rs index 50fe205..266ae6b 100644 --- a/build.rs +++ b/build.rs @@ -77,7 +77,8 @@ fn generate_tests(f: &mut File, path_so_far: PathBuf) -> std::io::Result<()> { f, " let ir = syntax.type_infer().expect(\"example is typed correctly\");" )?; - writeln!(f, " let ir_result = ir.eval();")?; + writeln!(f, " let ir_evaluator = crate::ir::Evaluator::default();")?; + writeln!(f, " let ir_result = ir_evaluator.eval(ir.clone());")?; writeln!(f, " match (&syntax_result, &ir_result) {{")?; writeln!(f, " (Err(e1), Err(e2)) => assert_eq!(e1, e2),")?; writeln!(f, " (Ok((v1, o1)), Ok((v2, o2))) => {{")?; diff --git a/examples/basic/generated0004.ngr b/examples/basic/generated0004.ngr new file mode 100644 index 0000000..736e241 --- /dev/null +++ b/examples/basic/generated0004.ngr @@ -0,0 +1,150 @@ +d3347 = { + s3349 = v3348 = 17175152522826808410u64; + print s3349; + j3350 = -6926831316600240717i64; + g3351 = b3352 = print s3349; + g3351; + u3353 = v3348; + c3460 = p3354 = { + w3441 = u3355 = k3356 = v3357 = { + j3364 = b3358 = b3359 = { + e3363 = o3360 = a3361 = n3362 = v3348 * v3348; + print e3363; + a3361 + }; + v3365 = j3364; + o3376 = { + p3366 = 62081u16; + t3369 = { + k3367 = 3742184609455079849u64; + y3368 = print g3351; + k3367 + }; + e3371 = o3370 = p3366; + print s3349; + l3372 = 50u8; + g3373 = 1086766998u32; + u3374 = g3373; + p3375 = 13826883074707422152u64 + }; + h3379 = v3377 = h3378 = 1513207896u32; + x3382 = f3380 = i3381 = -72i8; + a3383 = g3351; + q3440 = q3384 = { + r3385 = v3365; + z3437 = n3386 = { + o3387 = -1428233008i32; + c3388 = s3349; + b3352; + c3389 = c3388; + a3383; + c3390 = 1056u16; + l3433 = { + b3392 = f3391 = -881200191i32; + print o3376; + print v3377; + h3395 = y3393 = j3394 = -2456592492064497053i64; + c3396 = c3388; + f3397 = 2442824079u32; + d3428 = { + n3400 = h3398 = m3399 = v3365; + j3401 = v3348; + t3402 = -10i8; + e3403 = g3404 = print r3385; + e3403; + d3425 = { + u3405 = 51313u16; + l3406 = 235u8; + l3407 = 7030u16; + i3413 = { + b3352; + v3408 = o3387; + i3409 = 42u8; + q3411 = { + u3410 = -70i8; + n3400 + }; + q3412 = print i3409; + x3382 + }; + z3414 = b3392; + j3417 = s3415 = o3416 = 7220082853233268797u64; + print i3381; + n3419 = a3418 = -1270109327i32; + o3420 = r3385; + o3421 = x3382; + j3422 = print i3381; + q3423 = -4497i16; + x3424 = -98995788i32; + f3391 + }; + { + x3426 = r3385 - 13032422114254415490u64; + e3427 = -51i8; + j3401 + } + }; + k3429 = c3396; + print j3350; + g3351; + f3430 = -12293i16; + v3431 = 4016608549u32; + t3432 = f3397; + f3391 - -716040069i32 + }; + r3434 = -12984i16; + s3435 = 293908485953501586u64; + { + print j3364; + h3436 = 7732092399687242928u64 + } + }; + u3438 = v3377 * 3168795739u32; + p3439 = -12313i16; + u3438 + }; + v3348 + }; + x3442 = -5279281110772475785i64; + y3443 = v3348; + b3444 = 2732851783u32; + l3456 = l3445 = { + v3446 = -27499i16; + i3447 = 3517560837u32; + z3448 = u3353 * u3355; + u3449 = x3442; + t3450 = u3353; + m3452 = n3451 = u3355; + b3453 = m3452; + x3455 = t3454 = 2134443760u32; + print b3444; + b3444 + }; + c3457 = l3456; + g3351; + m3458 = 1822439673528019141u64; + d3459 = l3456 + }; + k3462 = f3461 = 13u8; + g3351; + u3463 = -10083i16; + v3348 +}; +y3464 = { + y3465 = 163u8; + y3466 = -7760i16; + e3467 = d3347; + q3468 = 58708u16; + -426970972827051249i64 +}; +t3469 = 524885465u32; +function b3470 (y3471,c3472,u3473) 1606677228u32; +y3464; +-240502590i32; +v3474 = z3475 = { + t3476 = y3464; + p3477 = t3469; + i3478 = p3477; + a3480 = e3479 = t3476; + p3477 +}; \ No newline at end of file diff --git a/src/backend/eval.rs b/src/backend/eval.rs index fce0da9..227a48a 100644 --- a/src/backend/eval.rs +++ b/src/backend/eval.rs @@ -120,15 +120,6 @@ proptest::proptest! { #[test] fn static_backend(program in Program::arbitrary()) { use crate::eval::PrimOpError; - - let basic_result = program.eval().map(|(_,x)| x); - - // windows `printf` is going to terminate lines with "\r\n", so we need to adjust - // our test result here. - #[cfg(target_family="windows")] - let basic_result = basic_result.map(|x| x.replace('\n', "\r\n")); - - if !matches!(basic_result, Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))) { use pretty::DocAllocator; let allocator = pretty::Arena::new(); let result = allocator.text("-------------") @@ -138,6 +129,28 @@ proptest::proptest! { result.render_raw(70, &mut pretty::IoWrite::new(std::io::stdout())) .expect("rendering works"); + + eprintln!("BEFORE EVAL"); + let ir_evaluator = crate::ir::Evaluator::default(); + let basic_result = ir_evaluator.eval(program.clone()).map(|(_,o)| o); + eprintln!("AFTER EVAL"); + + // windows `printf` is going to terminate lines with "\r\n", so we need to adjust + // our test result here. + #[cfg(target_family="windows")] + let basic_result = basic_result.map(|x| x.replace('\n', "\r\n")); + + if !matches!(basic_result, Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))) { + //use pretty::DocAllocator; + //let allocator = pretty::Arena::new(); + //let result = allocator.text("-------------") + // .append(allocator.line()) + // .append(program.pretty(&allocator)) + // .append(allocator.line()); + //result.render_raw(70, &mut pretty::IoWrite::new(std::io::stdout())) + // .expect("rendering works"); + + eprintln!("BEFORE OM EVAL"); let compiled_result = Backend::::eval(program); proptest::prop_assert_eq!(basic_result, compiled_result); } @@ -159,7 +172,8 @@ proptest::proptest! { // .render_colored(70, pretty::termcolor::StandardStream::stdout(pretty::termcolor::ColorChoice::Auto)) // .expect("rendering works"); - let basic_result = program.eval().map(|(_,x)| x); + let ir_evaluator = crate::ir::Evaluator::default(); + let basic_result = ir_evaluator.eval(program.clone()).map(|(_,o)| o); if !matches!(basic_result, Err(EvalError::PrimOp(PrimOpError::MathFailure(_)))) { let compiled_result = Backend::::eval(program); diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index a92a90c..68f98f7 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -500,7 +500,7 @@ impl Backend { .into_iter() .unzip(); - match *function { + match function { ValueOrRef::Value(_, _, _) => { panic!("Can't use a value for a function") } @@ -568,10 +568,7 @@ impl Backend { Primitive::Negate => { assert_eq!(1, arguments.len()); - Ok(( - builder.ins().ineg(arguments[0]), - argument_types[0], - )) + Ok((builder.ins().ineg(arguments[0]), argument_types[0])) } Primitive::Print => { diff --git a/src/bin/ngrun.rs b/src/bin/ngrun.rs index 80a06bb..0cc42ec 100644 --- a/src/bin/ngrun.rs +++ b/src/bin/ngrun.rs @@ -102,7 +102,8 @@ fn main() { }; if cli.interpreter == Interpreter::IR { - match ir.eval() { + let evaluator = ngr::ir::Evaluator::default(); + match evaluator.eval(ir) { Err(e) => tracing::error!(error = %e, "Evaluation error"), Ok(v) => print_result(v), } diff --git a/src/eval/primtype.rs b/src/eval/primtype.rs index 19a8dd4..c714627 100644 --- a/src/eval/primtype.rs +++ b/src/eval/primtype.rs @@ -250,7 +250,7 @@ impl PrimitiveType { &[("+", 2), ("-", 2), ("*", 2), ("/", 2)] } PrimitiveType::I8 | PrimitiveType::I16 | PrimitiveType::I32 | PrimitiveType::I64 => { - &[("+", 2), ("-", 1), ("-", 2), ("*", 2), ("/", 2)] + &[("+", 2), ("negate", 1), ("-", 2), ("*", 2), ("/", 2)] } } } diff --git a/src/ir.rs b/src/ir.rs index 5308405..4b80658 100644 --- a/src/ir.rs +++ b/src/ir.rs @@ -21,3 +21,4 @@ mod strings; mod top_level; pub use ast::*; +pub use eval::Evaluator; diff --git a/src/ir/arbitrary.rs b/src/ir/arbitrary.rs index 072afc2..182c43b 100644 --- a/src/ir/arbitrary.rs +++ b/src/ir/arbitrary.rs @@ -359,11 +359,7 @@ fn generate_random_expression( Expression::Call( Location::manufactured(), out_type, - Box::new(ValueOrRef::Primitive( - Location::manufactured(), - primtype, - primop, - )), + ValueOrRef::Primitive(Location::manufactured(), primtype, primop), args, ) } @@ -393,11 +389,7 @@ fn generate_random_expression( Expression::Call( Location::manufactured(), Type::void(), - Box::new(ValueOrRef::Primitive( - Location::manufactured(), - Type::void(), - Primitive::Print, - )), + ValueOrRef::Primitive(Location::manufactured(), Type::void(), Primitive::Print), vec![ValueOrRef::Ref( Location::manufactured(), var_type.clone(), diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 24ab128..9699fa3 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -114,7 +114,7 @@ pub enum Expression { ), FieldRef(Location, Type, Type, ValueOrRef, ArcIntern), Block(Location, Type, Vec>), - Call(Location, Type, Box>, Vec>), + Call(Location, Type, ValueOrRef, Vec>), Bind(Location, Variable, Type, Box>), } @@ -129,7 +129,7 @@ impl Expression { Expression::FieldRef(_, t, _, _, _) => t.clone(), Expression::Block(_, t, _) => t.clone(), Expression::Call(_, t, _, _) => t.clone(), - Expression::Bind(_, _, _, _) => Type::void(), + Expression::Bind(_, _, t, _) => t.clone(), } } diff --git a/src/ir/eval.rs b/src/ir/eval.rs index d74db44..aa88e21 100644 --- a/src/ir/eval.rs +++ b/src/ir/eval.rs @@ -3,60 +3,74 @@ use crate::eval::{EvalError, Value}; use crate::ir::{Expression, Program, TopLevel, Variable}; use crate::util::scoped_map::ScopedMap; use std::collections::HashMap; +use std::fmt::Display; type IRValue = Value>; type IREvalError = EvalError>; -impl> Program { +pub struct Evaluator { + env: ScopedMap>, + stdout: String, +} + +impl Default for Evaluator { + fn default() -> Self { + Evaluator { + env: ScopedMap::new(), + stdout: String::new(), + } + } +} + +impl Evaluator +where + T: Clone + Into, + Expression: Display, +{ /// Evaluate the program, returning either an error or the result of the final /// statement and the complete contents of the console output. /// /// The print outs will be newline separated, with one print out per line. - pub fn eval(&self) -> Result<(IRValue, String), IREvalError> { - let mut env: ScopedMap> = ScopedMap::new(); - let mut stdout = String::new(); + pub fn eval(mut self, program: Program) -> Result<(IRValue, String), IREvalError> { let mut last_value = Value::Void; - for stmt in self.items.iter() { + for stmt in program.items.into_iter() { match stmt { TopLevel::Function(name, args, _, body) => { let closure = Value::Closure( Some(name.clone()), - env.clone(), + self.env.clone(), args.iter().map(|(x, _)| x.clone()).collect(), body.clone(), ); - env.insert(name.clone(), closure.clone()); + self.env.insert(name.clone(), closure.clone()); last_value = closure; } TopLevel::Statement(expr) => { - last_value = expr.eval(&mut env, &mut stdout)?; + last_value = self.eval_expr(expr)?; } } } - Ok((last_value, stdout)) + Ok((last_value, self.stdout)) } -} -impl Expression -where - T: Clone + Into, -{ - fn eval( - &self, - env: &mut ScopedMap>, - stdout: &mut String, - ) -> Result, IREvalError> { - match self { - Expression::Atomic(x) => x.eval(env), + /// Get the current output of the evaluated program. + pub fn stdout(self) -> String { + self.stdout + } + + fn eval_expr(&mut self, expr: Expression) -> Result, IREvalError> { + println!("evaluating {}", expr); + match expr { + Expression::Atomic(x) => self.eval_atomic(x), Expression::Cast(_, t, valref) => { - let value = valref.eval(env)?; - let ty = t.clone().into(); + let value = self.eval_atomic(valref)?; + let ty = t.into(); match ty { Type::Primitive(pt) => Ok(pt.safe_cast(&value)?), @@ -68,15 +82,15 @@ where Expression::Construct(_, _, name, fields) => { let mut result_fields = HashMap::with_capacity(fields.len()); - for (name, subexpr) in fields.iter() { - result_fields.insert(name.clone(), subexpr.eval(env)?); + for (name, subexpr) in fields.into_iter() { + result_fields.insert(name.clone(), self.eval_atomic(subexpr)?); } Ok(Value::Structure(Some(name.clone()), result_fields)) } - Expression::FieldRef(loc, _, _, valref, field) => match valref.eval(env)? { - Value::Structure(oname, mut fields) => match fields.remove(field) { + Expression::FieldRef(loc, _, _, valref, field) => match self.eval_atomic(valref)? { + Value::Structure(oname, mut fields) => match fields.remove(&field) { None => Err(EvalError::NoFieldForValue( loc.clone(), Value::Structure(oname, fields), @@ -91,21 +105,21 @@ where Expression::Block(_, _, stmts) => { let mut result = Value::Void; - for stmt in stmts.iter() { - result = stmt.eval(env, stdout)?; + for stmt in stmts.into_iter() { + result = self.eval_expr(stmt)?; } Ok(result) } Expression::Bind(_, name, _, value) => { - let value = value.eval(env, stdout)?; - env.insert(name.clone(), value.clone()); + let value = self.eval_expr(*value)?; + self.env.insert(name.clone(), value.clone()); Ok(value) } Expression::Call(loc, _, fun, args) => { - let function = fun.eval(env)?; + let function = self.eval_atomic(fun)?; match function { Value::Closure(name, mut closure_env, arguments, body) => { @@ -120,20 +134,29 @@ where closure_env.new_scope(); for (name, value) in arguments.into_iter().zip(args) { - let value = value.eval(env)?; + let value = self.eval_atomic(value)?; closure_env.insert(name, value); } - let result = body.eval(&mut closure_env, stdout)?; + + let temp_ref = &mut closure_env; + + std::mem::swap(&mut self.env, temp_ref); + let result = self.eval_expr(body); + std::mem::swap(&mut self.env, temp_ref); closure_env.release_scope(); - Ok(result) + result } Value::Primitive(name) if name == "print" => { if let [ValueOrRef::Ref(loc, ty, name)] = &args[..] { - let value = ValueOrRef::Ref(loc.clone(), ty.clone(), name.clone()).eval(env)?; + let value = self.eval_atomic(ValueOrRef::Ref( + loc.clone(), + ty.clone(), + name.clone(), + ))?; let addendum = format!("{} = {}\n", name, value); - stdout.push_str(&addendum); + self.stdout.push_str(&addendum); Ok(Value::Void) } else { panic!("Non-reference/non-singleton argument to 'print'"); @@ -142,8 +165,8 @@ where Value::Primitive(name) => { let values = args - .iter() - .map(|x| x.eval(env)) + .into_iter() + .map(|x| self.eval_atomic(x)) .collect::>()?; println!("primitive {}: args {:?}", name, values); Value::calculate(name.as_str(), values).map_err(Into::into) @@ -154,25 +177,24 @@ where } } } -} -impl ValueOrRef { - fn eval(&self, env: &ScopedMap>) -> Result, IREvalError> { - match self { + fn eval_atomic(&self, value: ValueOrRef) -> Result, IREvalError> { + match value { ValueOrRef::Value(_, _, v) => match v { - super::Value::I8(_, v) => Ok(Value::I8(*v)), - super::Value::I16(_, v) => Ok(Value::I16(*v)), - super::Value::I32(_, v) => Ok(Value::I32(*v)), - super::Value::I64(_, v) => Ok(Value::I64(*v)), - super::Value::U8(_, v) => Ok(Value::U8(*v)), - super::Value::U16(_, v) => Ok(Value::U16(*v)), - super::Value::U32(_, v) => Ok(Value::U32(*v)), - super::Value::U64(_, v) => Ok(Value::U64(*v)), + super::Value::I8(_, v) => Ok(Value::I8(v)), + super::Value::I16(_, v) => Ok(Value::I16(v)), + super::Value::I32(_, v) => Ok(Value::I32(v)), + super::Value::I64(_, v) => Ok(Value::I64(v)), + super::Value::U8(_, v) => Ok(Value::U8(v)), + super::Value::U16(_, v) => Ok(Value::U16(v)), + super::Value::U32(_, v) => Ok(Value::U32(v)), + super::Value::U64(_, v) => Ok(Value::U64(v)), super::Value::Void => Ok(Value::Void), }, - ValueOrRef::Ref(loc, _, n) => env - .get(n) + ValueOrRef::Ref(loc, _, n) => self + .env + .get(&n) .cloned() .ok_or_else(|| EvalError::LookupFailed(loc.clone(), n.to_string())), @@ -185,8 +207,9 @@ impl ValueOrRef { fn two_plus_three() { let input = crate::syntax::Program::parse(0, "x = 2 + 3; print x;").expect("parse works"); let ir = input.type_infer().expect("test should be type-valid"); - let (_, output) = ir.eval().expect("runs successfully"); - assert_eq!("x = 5u64\n", &output); + let evaluator = Evaluator::default(); + let (_, result) = evaluator.eval(ir).expect("runs successfully"); + assert_eq!("x = 5u64\n", &result); } #[test] @@ -194,6 +217,7 @@ fn lotsa_math() { let input = crate::syntax::Program::parse(0, "x = 2 + 3 * 10 / 5 - 1; print x;").expect("parse works"); let ir = input.type_infer().expect("test should be type-valid"); - let (_, output) = ir.eval().expect("runs successfully"); - assert_eq!("x = 7u64\n", &output); + let evaluator = Evaluator::default(); + let (_, result) = evaluator.eval(ir).expect("runs successfully"); + assert_eq!("x = 7u64\n", &result); } diff --git a/src/syntax.rs b/src/syntax.rs index 26d0b36..6dd75d2 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -46,11 +46,9 @@ pub use crate::syntax::ast::*; pub use crate::syntax::location::Location; pub use crate::syntax::parser::{ProgramParser, TopLevelParser}; pub use crate::syntax::tokens::{LexerError, Token}; -#[cfg(test)] -use ::pretty::Arena; use lalrpop_util::ParseError; #[cfg(test)] -use proptest::{arbitrary::Arbitrary, prop_assert, prop_assert_eq}; +use proptest::{arbitrary::Arbitrary, prop_assert}; use std::ops::Range; #[cfg(test)] use std::str::FromStr; @@ -324,27 +322,6 @@ fn order_of_operations() { } proptest::proptest! { - #[test] - fn random_render_parses_equal(program: Program) { - let mut file_database = SimpleFiles::new(); - let writer = ::pretty::termcolor::StandardStream::stderr(::pretty::termcolor::ColorChoice::Auto); - let config = codespan_reporting::term::Config::default(); - let allocator = Arena::<()>::new(); - - let mut out_vector = vec![]; - prop_assert!(program.pretty(&allocator).render(80, &mut out_vector).is_ok()); - let string = std::str::from_utf8(&out_vector).expect("emitted valid string"); - let file_handle = file_database.add("test", string); - let file_db_info = file_database.get(file_handle).expect("find thing just inserted"); - let parsed = Program::parse(file_handle, file_db_info.source()); - - if let Err(e) = &parsed { - eprintln!("failed to parse:\n{}", string); - codespan_reporting::term::emit(&mut writer.lock(), &config, &file_database, &e.into()).unwrap(); - } - prop_assert_eq!(program, parsed.unwrap()); - } - #[test] fn random_syntaxes_validate(program: Program) { let (errors, _) = program.validate(); @@ -354,6 +331,8 @@ proptest::proptest! { #[test] fn generated_run_or_overflow(program in Program::arbitrary_with(GenerationEnvironment::new(false))) { use crate::eval::{EvalError, PrimOpError}; + println!("-----------\nprogram:\n{}\n", program); + println!("-----------\nresult:\n{:?}\n", program.eval()); prop_assert!(matches!(program.eval(), Ok(_) | Err(EvalError::PrimOp(PrimOpError::MathFailure(_))))); } } diff --git a/src/syntax/arbitrary.rs b/src/syntax/arbitrary.rs index 44bd323..e9ab301 100644 --- a/src/syntax/arbitrary.rs +++ b/src/syntax/arbitrary.rs @@ -15,7 +15,7 @@ impl ConstantType { match self { ConstantType::Void => &[], ConstantType::I8 | ConstantType::I16 | ConstantType::I32 | ConstantType::I64 => { - &[("+", 2), ("-", 1), ("-", 2), ("*", 2), ("/", 2)] + &[("+", 2), ("negate", 1), ("-", 2), ("*", 2), ("/", 2)] } ConstantType::U8 | ConstantType::U16 | ConstantType::U32 | ConstantType::U64 => { &[("+", 2), ("-", 2), ("*", 2), ("/", 2)] diff --git a/src/syntax/eval.rs b/src/syntax/eval.rs index f4635f7..d6185ec 100644 --- a/src/syntax/eval.rs +++ b/src/syntax/eval.rs @@ -146,7 +146,8 @@ impl Expression { Value::Primitive(name) if name == "print" => { if let [Expression::Reference(_, name)] = &args[..] { - let value = Expression::Reference(loc.clone(), name.clone()).eval(stdout, env)?; + let value = Expression::Reference(loc.clone(), name.clone()) + .eval(stdout, env)?; let value = match value { Value::Number(x) => Value::U64(x), x => x, @@ -156,7 +157,10 @@ impl Expression { stdout.push_str(&addendum); Ok(Value::Void) } else { - panic!("Non-reference/non-singleton argument to 'print': {:?}", args); + panic!( + "Non-reference/non-singleton argument to 'print': {:?}", + args + ); } } diff --git a/src/type_infer.rs b/src/type_infer.rs index 31916dd..475bf9f 100644 --- a/src/type_infer.rs +++ b/src/type_infer.rs @@ -44,7 +44,8 @@ proptest::proptest! { fn translation_maintains_semantics(input in syntax::Program::arbitrary_with(GenerationEnvironment::new(false))) { let syntax_result = input.eval().map(|(x,o)| (x.strip(), o)); let ir = input.type_infer().expect("arbitrary should generate type-safe programs"); - let ir_result = ir.eval().map(|(x,o)| (x.strip(), o)); + let ir_evaluator = crate::ir::Evaluator::default(); + let ir_result = ir_evaluator.eval(ir).map(|(x, o)| (x.strip(), o)); match (syntax_result, ir_result) { (Err(e1), Err(e2)) => proptest::prop_assert_eq!(e1, e2), (Ok((v1, o1)), Ok((v2, o2))) => { diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index fb69cba..02cb653 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -360,10 +360,8 @@ fn convert_expression( let arg_type = ir::TypeOrVar::new_located(loc.clone()); constraint_db.push(Constraint::NumericType(loc.clone(), arg_type.clone())); constraint_db.push(Constraint::IsSigned(loc.clone(), arg_type.clone())); - let funtype = ir::TypeOrVar::Function( - vec![arg_type.clone()], - Box::new(arg_type), - ); + let funtype = + ir::TypeOrVar::Function(vec![arg_type.clone()], Box::new(arg_type)); let result_value = ir::ValueOrRef::Primitive(loc, funtype.clone(), primop); (ir::Expression::Atomic(result_value), funtype) } @@ -409,8 +407,7 @@ fn convert_expression( }) .collect(); - let last_call = - ir::Expression::Call(loc.clone(), return_type.clone(), Box::new(fun), new_args); + let last_call = ir::Expression::Call(loc.clone(), return_type.clone(), fun, new_args); (finalize_expressions(prereqs, last_call), return_type) } diff --git a/src/type_infer/finalize.rs b/src/type_infer/finalize.rs index 7f12adb..624bbe8 100644 --- a/src/type_infer/finalize.rs +++ b/src/type_infer/finalize.rs @@ -91,7 +91,7 @@ fn finalize_expression( Expression::Call(loc, ty, fun, args) => Expression::Call( loc, finalize_type(ty, resolutions), - Box::new(finalize_val_or_ref(*fun, resolutions)), + finalize_val_or_ref(fun, resolutions), args.into_iter() .map(|x| finalize_val_or_ref(x, resolutions)) .collect(), -- 2.53.0 From 383d9185bfcfa0ece9e19cdd9a8df6c53826056d Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 20 Apr 2024 20:47:34 -0400 Subject: [PATCH 50/59] Clean up expression sizes, which seems to fix stack blowouts. --- src/backend/eval.rs | 19 ++++++++----------- src/backend/into_crane.rs | 2 +- src/ir/ast.rs | 19 +++++++++++++++++++ src/ir/eval.rs | 2 -- src/ir/fields.rs | 15 +++++---------- src/syntax.rs | 2 -- src/syntax/validate.rs | 1 - src/type_infer/convert.rs | 2 +- 8 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/backend/eval.rs b/src/backend/eval.rs index 227a48a..4aabe7e 100644 --- a/src/backend/eval.rs +++ b/src/backend/eval.rs @@ -120,20 +120,18 @@ proptest::proptest! { #[test] fn static_backend(program in Program::arbitrary()) { use crate::eval::PrimOpError; - use pretty::DocAllocator; - let allocator = pretty::Arena::new(); - let result = allocator.text("-------------") - .append(allocator.line()) - .append(program.pretty(&allocator)) - .append(allocator.line()); - result.render_raw(70, &mut pretty::IoWrite::new(std::io::stdout())) - .expect("rendering works"); + // use pretty::DocAllocator; + // let allocator = pretty::Arena::new(); + // let result = allocator.text("-------------") + // .append(allocator.line()) + // .append(program.pretty(&allocator)) + // .append(allocator.line()); + // result.render_raw(70, &mut pretty::IoWrite::new(std::io::stdout())) + // .expect("rendering works"); - eprintln!("BEFORE EVAL"); let ir_evaluator = crate::ir::Evaluator::default(); let basic_result = ir_evaluator.eval(program.clone()).map(|(_,o)| o); - eprintln!("AFTER EVAL"); // windows `printf` is going to terminate lines with "\r\n", so we need to adjust // our test result here. @@ -150,7 +148,6 @@ proptest::proptest! { //result.render_raw(70, &mut pretty::IoWrite::new(std::io::stdout())) // .expect("rendering works"); - eprintln!("BEFORE OM EVAL"); let compiled_result = Backend::::eval(program); proptest::prop_assert_eq!(basic_result, compiled_result); } diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index 68f98f7..68a7774 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -100,7 +100,7 @@ impl Backend { false, )?; tracing::info!(name = %top_level_name, "defining top-level data structure"); - self.module.define_data(data_id, fields.blank_data())?; + self.module.define_data(data_id, &fields.blank_data())?; let pointer = self.module.target_config().pointer_type(); self.defined_symbols .insert(top_level_name, (data_id, pointer)); diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 9699fa3..24d7c41 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -482,3 +482,22 @@ impl TryFrom for Type { } } } + +#[test] +fn struct_sizes_are_rational() { + assert_eq!(8, std::mem::size_of::>()); + assert_eq!(24, std::mem::size_of::()); + assert_eq!(1, std::mem::size_of::()); + assert_eq!(1, std::mem::size_of::()); + assert_eq!(32, std::mem::size_of::>()); + assert_eq!(40, std::mem::size_of::()); + assert_eq!(40, std::mem::size_of::()); + assert_eq!(80, std::mem::size_of::>()); + assert_eq!(80, std::mem::size_of::>()); + assert_eq!(200, std::mem::size_of::>()); + assert_eq!(200, std::mem::size_of::>()); + assert_eq!(272, std::mem::size_of::>()); + assert_eq!(272, std::mem::size_of::>()); + assert_eq!(72, std::mem::size_of::>()); + assert_eq!(72, std::mem::size_of::>()); +} \ No newline at end of file diff --git a/src/ir/eval.rs b/src/ir/eval.rs index aa88e21..9a42560 100644 --- a/src/ir/eval.rs +++ b/src/ir/eval.rs @@ -64,7 +64,6 @@ where } fn eval_expr(&mut self, expr: Expression) -> Result, IREvalError> { - println!("evaluating {}", expr); match expr { Expression::Atomic(x) => self.eval_atomic(x), @@ -168,7 +167,6 @@ where .into_iter() .map(|x| self.eval_atomic(x)) .collect::>()?; - println!("primitive {}: args {:?}", name, values); Value::calculate(name.as_str(), values).map_err(Into::into) } diff --git a/src/ir/fields.rs b/src/ir/fields.rs index f59ae43..239f1cf 100644 --- a/src/ir/fields.rs +++ b/src/ir/fields.rs @@ -6,7 +6,6 @@ use std::fmt; pub struct Fields { ordering: FieldOrdering, total_size: usize, - cranelift_description: Option, fields: Vec<(ArcIntern, T)>, } @@ -41,7 +40,6 @@ impl Fields { Fields { ordering, total_size: 0, - cranelift_description: None, fields: vec![], } } @@ -69,7 +67,6 @@ impl Fields { Fields { ordering: self.ordering, total_size: self.total_size, - cranelift_description: self.cranelift_description, fields: self.fields.into_iter().map(|(n, t)| (n, f(t))).collect(), } } @@ -111,13 +108,11 @@ impl Fields { self.fields.iter_mut().map(|(_, x)| x) } - pub fn blank_data(&mut self) -> &DataDescription { - self.cranelift_description.get_or_insert_with(|| { - let mut cranelift_description = DataDescription::new(); - cranelift_description.set_align(8); - cranelift_description.define_zeroinit(self.total_size); - cranelift_description - }) + pub fn blank_data(&mut self) -> DataDescription { + let mut cranelift_description = DataDescription::new(); + cranelift_description.set_align(8); + cranelift_description.define_zeroinit(self.total_size); + cranelift_description } pub fn field_type_and_offset(&self, field: &ArcIntern) -> Option<(&T, i32)> { diff --git a/src/syntax.rs b/src/syntax.rs index 6dd75d2..309f7ec 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -331,8 +331,6 @@ proptest::proptest! { #[test] fn generated_run_or_overflow(program in Program::arbitrary_with(GenerationEnvironment::new(false))) { use crate::eval::{EvalError, PrimOpError}; - println!("-----------\nprogram:\n{}\n", program); - println!("-----------\nresult:\n{:?}\n", program.eval()); prop_assert!(matches!(program.eval(), Ok(_) | Err(EvalError::PrimOp(PrimOpError::MathFailure(_))))); } } diff --git a/src/syntax/validate.rs b/src/syntax/validate.rs index 8ee2792..c95b2bc 100644 --- a/src/syntax/validate.rs +++ b/src/syntax/validate.rs @@ -68,7 +68,6 @@ impl Program { /// actually a problem. pub fn validate(&self) -> (Vec, Vec) { let mut bound_variables = ScopedMap::new(); - println!("validate:\n{}", self); self.validate_with_bindings(&mut bound_variables) } diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 02cb653..a8fe5c0 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -416,7 +416,7 @@ fn convert_expression( let mut ret_type = ir::TypeOrVar::Primitive(PrimitiveType::Void); let mut exprs = vec![]; - for xpr in stmts { + for xpr in stmts.into_iter() { let (expr, expr_type) = convert_expression(xpr, constraint_db, renames, bindings); ret_type = expr_type; -- 2.53.0 From c8d6719eb73b9faadc9cba7b93d26ced53b7daf8 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sat, 20 Apr 2024 20:51:10 -0400 Subject: [PATCH 51/59] yay conformity --- examples/basic/{type_checker1.ngr => type_infer0001.ngr} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/basic/{type_checker1.ngr => type_infer0001.ngr} (100%) diff --git a/examples/basic/type_checker1.ngr b/examples/basic/type_infer0001.ngr similarity index 100% rename from examples/basic/type_checker1.ngr rename to examples/basic/type_infer0001.ngr -- 2.53.0 From 52d5c9252be35b021f2ecc3dedd71b8de423ceef Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Mon, 22 Apr 2024 20:49:44 -0700 Subject: [PATCH 52/59] checkpoint --- examples/basic/function0004.ngr | 10 + src/ir/ast.rs | 4 +- src/syntax/ast.rs | 20 +- src/syntax/eval.rs | 35 +- src/syntax/parser.lalrpop | 13 +- src/syntax/pretty.rs | 82 +- src/syntax/validate.rs | 27 +- src/type_infer.rs | 62 +- src/type_infer/constraint.rs | 79 ++ src/type_infer/convert.rs | 945 +++++++++++----------- src/type_infer/error.rs | 146 ++++ src/type_infer/finalize.rs | 86 +- src/type_infer/result.rs | 50 ++ src/type_infer/solve.rs | 1306 ++++++++++++------------------- src/type_infer/warning.rs | 21 + 15 files changed, 1528 insertions(+), 1358 deletions(-) create mode 100644 examples/basic/function0004.ngr create mode 100644 src/type_infer/constraint.rs create mode 100644 src/type_infer/error.rs create mode 100644 src/type_infer/result.rs create mode 100644 src/type_infer/warning.rs diff --git a/examples/basic/function0004.ngr b/examples/basic/function0004.ngr new file mode 100644 index 0000000..6803641 --- /dev/null +++ b/examples/basic/function0004.ngr @@ -0,0 +1,10 @@ +function make_adder(x) + function (y) + x + y; + +add1 = make_adder(1); +add2 = make_adder(2); +one_plus_one = add1(1); +one_plus_three = add1(3); +print one_plus_one; +print one_plus_three; \ No newline at end of file diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 24d7c41..bb88f4a 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -74,6 +74,8 @@ impl Arbitrary for Program { #[derive(Clone, Debug)] pub enum TopLevel { Statement(Expression), + // FIXME: Is the return type actually necessary, given we can infer it from + // the expression type? Function(Variable, Vec<(Variable, Type)>, Type, Expression), } @@ -500,4 +502,4 @@ fn struct_sizes_are_rational() { assert_eq!(272, std::mem::size_of::>()); assert_eq!(72, std::mem::size_of::>()); assert_eq!(72, std::mem::size_of::>()); -} \ No newline at end of file +} diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 726ad5a..e41d443 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -27,12 +27,6 @@ pub struct Program { #[derive(Clone, Debug, PartialEq)] pub enum TopLevel { Expression(Expression), - Function( - Option, - Vec<(Name, Option)>, - Option, - Expression, - ), Structure(Location, Name, Vec<(Name, Type)>), } @@ -103,6 +97,13 @@ pub enum Expression { Call(Location, Box, Vec), Block(Location, Vec), Binding(Location, Name, Box), + Function( + Location, + Option, + Vec<(Name, Option)>, + Option, + Box, + ), } impl Expression { @@ -157,6 +158,12 @@ impl PartialEq for Expression { Expression::Binding(_, name2, expr2) => name1 == name2 && expr1 == expr2, _ => false, }, + Expression::Function(_, mname1, args1, mret1, body1) => match other { + Expression::Function(_, mname2, args2, mret2, body2) => { + mname1 == mname2 && args1 == args2 && mret1 == mret2 && body1 == body2 + } + _ => false, + }, } } } @@ -174,6 +181,7 @@ impl Expression { Expression::Call(loc, _, _) => loc, Expression::Block(loc, _) => loc, Expression::Binding(loc, _, _) => loc, + Expression::Function(loc, _, _, _, _) => loc, } } } diff --git a/src/syntax/eval.rs b/src/syntax/eval.rs index d6185ec..ae45ed8 100644 --- a/src/syntax/eval.rs +++ b/src/syntax/eval.rs @@ -24,22 +24,6 @@ impl Program { for stmt in self.items.iter() { match stmt { - TopLevel::Function(name, arg_names, _, body) => { - last_result = Value::Closure( - name.clone().map(Name::intern), - env.clone(), - arg_names - .iter() - .cloned() - .map(|(x, _)| Name::intern(x)) - .collect(), - body.clone(), - ); - if let Some(name) = name { - env.insert(name.clone().intern(), last_result.clone()); - } - } - TopLevel::Expression(expr) => last_result = expr.eval(&mut stdout, &mut env)?, TopLevel::Structure(_, _, _) => { @@ -191,6 +175,25 @@ impl Expression { env.insert(name.clone().intern(), actual_value.clone()); Ok(actual_value) } + + Expression::Function(_, name, arg_names, _, body) => { + let result = Value::Closure( + name.clone().map(Name::intern), + env.clone(), + arg_names + .iter() + .cloned() + .map(|(x, _)| Name::intern(x)) + .collect(), + *body.clone(), + ); + + if let Some(name) = name { + env.insert(name.clone().intern(), result.clone()); + } + + Ok(result) + } } } } diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index ed42ca4..4522807 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -79,16 +79,10 @@ ProgramTopLevel: Vec = { } pub TopLevel: TopLevel = { - => f, => s, ";" => TopLevel::Expression(s), } -Function: TopLevel = { - "function" "(" > ")" " Type)?> ";" => - TopLevel::Function(opt_name, args, ret.map(|x| x.1), exp), -} - Argument: (Name, Option) = { "> => (Name::new(v, Location::new(file_idx, name_start..name_end)), t.map(|v| v.1)), @@ -170,6 +164,13 @@ BindingExpression: Expression = { Box::new(e), ), + FunctionExpression, +} + +FunctionExpression: Expression = { + "function" "(" > ")" " Type)?> => + Expression::Function(Location::new(file_idx, s..e), opt_name, args, ret.map(|x| x.1), Box::new(exp)), + PrintExpression, } diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index 81eabe2..a9caf3b 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -21,47 +21,6 @@ impl TopLevel { pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { match self { TopLevel::Expression(expr) => expr.pretty(allocator), - TopLevel::Function(name, args, rettype, body) => allocator - .text("function") - .append(allocator.space()) - .append( - name.as_ref() - .map(|x| allocator.text(x.to_string())) - .unwrap_or_else(|| allocator.nil()), - ) - .append( - allocator - .intersperse( - args.iter().map(|(x, t)| { - allocator.text(x.to_string()).append( - t.as_ref() - .map(|t| { - allocator - .text(":") - .append(allocator.space()) - .append(t.pretty(allocator)) - }) - .unwrap_or_else(|| allocator.nil()), - ) - }), - allocator.text(","), - ) - .parens(), - ) - .append( - rettype - .as_ref() - .map(|rettype| { - allocator - .space() - .append(allocator.text("->")) - .append(allocator.space()) - .append(rettype.pretty(allocator)) - }) - .unwrap_or_else(|| allocator.nil()), - ) - .append(allocator.space()) - .append(body.pretty(allocator)), TopLevel::Structure(_, name, fields) => allocator .text("struct") .append(allocator.space()) @@ -148,6 +107,47 @@ impl Expression { .append(allocator.text("=")) .append(allocator.space()) .append(expr.pretty(allocator)), + Expression::Function(_, name, args, rettype, body) => allocator + .text("function") + .append(allocator.space()) + .append( + name.as_ref() + .map(|x| allocator.text(x.to_string())) + .unwrap_or_else(|| allocator.nil()), + ) + .append( + allocator + .intersperse( + args.iter().map(|(x, t)| { + allocator.text(x.to_string()).append( + t.as_ref() + .map(|t| { + allocator + .text(":") + .append(allocator.space()) + .append(t.pretty(allocator)) + }) + .unwrap_or_else(|| allocator.nil()), + ) + }), + allocator.text(","), + ) + .parens(), + ) + .append( + rettype + .as_ref() + .map(|rettype| { + allocator + .space() + .append(allocator.text("->")) + .append(allocator.space()) + .append(rettype.pretty(allocator)) + }) + .unwrap_or_else(|| allocator.nil()), + ) + .append(allocator.space()) + .append(body.pretty(allocator)), } } } diff --git a/src/syntax/validate.rs b/src/syntax/validate.rs index c95b2bc..afbba2c 100644 --- a/src/syntax/validate.rs +++ b/src/syntax/validate.rs @@ -84,9 +84,6 @@ impl Program { let mut warnings = vec![]; for stmt in self.items.iter() { - if let TopLevel::Function(Some(name), _, _, _) = stmt { - bound_variables.insert(name.to_string(), name.location.clone()); - } let (mut new_errors, mut new_warnings) = stmt.validate_with_bindings(bound_variables); errors.append(&mut new_errors); warnings.append(&mut new_warnings); @@ -119,18 +116,6 @@ impl TopLevel { bound_variables: &mut ScopedMap, ) -> (Vec, Vec) { match self { - TopLevel::Function(name, arguments, _, body) => { - bound_variables.new_scope(); - if let Some(name) = name { - bound_variables.insert(name.name.clone(), name.location.clone()); - } - for (arg, _) in arguments.iter() { - bound_variables.insert(arg.name.clone(), arg.location.clone()); - } - let result = body.validate(bound_variables); - bound_variables.release_scope(); - result - } TopLevel::Expression(expr) => expr.validate(bound_variables), TopLevel::Structure(_, _, _) => (vec![], vec![]), } @@ -214,6 +199,18 @@ impl Expression { (errors, warnings) } + Expression::Function(_, name, arguments, _, body) => { + if let Some(name) = name { + variable_map.insert(name.name.clone(), name.location.clone()); + } + variable_map.new_scope(); + for (arg, _) in arguments.iter() { + variable_map.insert(arg.name.clone(), arg.location.clone()); + } + let result = body.validate(variable_map); + variable_map.release_scope(); + result + } } } } diff --git a/src/type_infer.rs b/src/type_infer.rs index 475bf9f..94bb3cb 100644 --- a/src/type_infer.rs +++ b/src/type_infer.rs @@ -10,20 +10,43 @@ //! all the constraints we've generated. If that's successful, in the final phase, we //! do the final conversion to the IR AST, filling in any type information we've learned //! along the way. +mod constraint; mod convert; +mod error; mod finalize; +mod result; mod solve; +mod warning; -use self::convert::convert_program; -use self::finalize::finalize_program; -use self::solve::solve_constraints; -pub use self::solve::{TypeInferenceError, TypeInferenceResult, TypeInferenceWarning}; +use self::constraint::Constraint; +use self::error::TypeInferenceError; +pub use self::result::TypeInferenceResult; +use self::warning::TypeInferenceWarning; use crate::ir::ast as ir; use crate::syntax; #[cfg(test)] use crate::syntax::arbitrary::GenerationEnvironment; +use internment::ArcIntern; #[cfg(test)] use proptest::prelude::Arbitrary; +use std::collections::HashMap; + +#[derive(Default)] +struct InferenceEngine { + constraints: Vec, + type_definitions: HashMap, ir::TypeOrVar>, + variable_types: HashMap, ir::TypeOrVar>, + functions: HashMap< + ArcIntern, + ( + Vec<(ArcIntern, ir::TypeOrVar)>, + ir::Expression, + ), + >, + statements: Vec>, + errors: Vec, + warnings: Vec, +} impl syntax::Program { /// Infer the types for the syntactic AST, returning either a type-checked program in @@ -32,10 +55,35 @@ impl syntax::Program { /// You really should have made sure that this program was validated before running /// this method, otherwise you may experience panics during operation. pub fn type_infer(self) -> TypeInferenceResult> { - let (program, constraint_db) = convert_program(self); - let inference_result = solve_constraints(&program.type_definitions, constraint_db); + let mut engine = InferenceEngine::default(); + engine.injest_program(self); + engine.solve_constraints(); - inference_result.map(|resolutions| finalize_program(program, &resolutions)) + if engine.errors.is_empty() { + let resolutions = std::mem::take(&mut engine.constraints) + .into_iter() + .map(|constraint| match constraint { + Constraint::Equivalent(_, ir::TypeOrVar::Variable(_, name), result) => { + match result.try_into() { + Err(e) => panic!("Ended up with complex type {}", e), + Ok(v) => (name, v), + } + } + _ => panic!("Had something that wasn't an equivalence left at the end!"), + }) + .collect(); + let warnings = std::mem::take(&mut engine.warnings); + + TypeInferenceResult::Success { + result: engine.finalize_program(resolutions), + warnings, + } + } else { + TypeInferenceResult::Failure { + errors: engine.errors, + warnings: engine.warnings, + } + } } } diff --git a/src/type_infer/constraint.rs b/src/type_infer/constraint.rs new file mode 100644 index 0000000..e6520fb --- /dev/null +++ b/src/type_infer/constraint.rs @@ -0,0 +1,79 @@ +use crate::ir::TypeOrVar; +use crate::syntax::Location; +use internment::ArcIntern; +use std::fmt; + +/// A type inference constraint that we're going to need to solve. +#[derive(Debug)] +pub enum Constraint { + /// The given type must be printable using the `print` built-in + Printable(Location, TypeOrVar), + /// The provided numeric value fits in the given constant type + FitsInNumType(Location, TypeOrVar, u64), + /// The given type can be casted to the target type safely + CanCastTo(Location, TypeOrVar, TypeOrVar), + /// The given type has the given field in it, and the type of that field + /// is as given. + TypeHasField(Location, TypeOrVar, ArcIntern, TypeOrVar), + /// The given type must be some numeric type, but this is not a constant + /// value, so don't try to default it if we can't figure it out + NumericType(Location, TypeOrVar), + /// The given type is attached to a constant and must be some numeric type. + /// If we can't figure it out, we should warn the user and then just use a + /// default. + ConstantNumericType(Location, TypeOrVar), + /// The two types should be equivalent + Equivalent(Location, TypeOrVar, TypeOrVar), + /// The given type can be resolved to something + IsSomething(Location, TypeOrVar), + /// The given type can be negated + IsSigned(Location, TypeOrVar), + /// Checks to see if the given named type is equivalent to the provided one. + NamedTypeIs(Location, ArcIntern, TypeOrVar), +} + +impl fmt::Display for Constraint { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Constraint::Printable(_, ty) => write!(f, "PRINTABLE {}", ty), + Constraint::FitsInNumType(_, ty, num) => write!(f, "FITS_IN {} {}", num, ty), + Constraint::CanCastTo(_, ty, ty2) => write!(f, "CAST {} -> {}", ty, ty2), + Constraint::TypeHasField(_, ty1, field, ty2) => { + write!(f, "FIELD {}.{} -> {}", ty1, field, ty2) + } + Constraint::NumericType(_, ty) => write!(f, "NUMERIC {}", ty), + Constraint::ConstantNumericType(_, ty) => write!(f, "CONST_NUMERIC {}", ty), + Constraint::Equivalent(_, ty, ty2) => write!(f, "EQUIVALENT {} => {}", ty, ty2), + Constraint::IsSomething(_, ty) => write!(f, "SOMETHING {}", ty), + Constraint::IsSigned(_, ty) => write!(f, "SIGNED {}", ty), + Constraint::NamedTypeIs(_, name, ty) => write!(f, "TYPE_EQUIV {} == {}", name, ty), + } + } +} + +impl Constraint { + /// Replace all instances of the name (anywhere! including on the left hand side of equivalences!) + /// with the given type. + /// + /// Returns whether or not anything was changed in the constraint. + pub fn replace(&mut self, name: &ArcIntern, replace_with: &TypeOrVar) -> bool { + match self { + Constraint::Printable(_, ty) => ty.replace(name, replace_with), + Constraint::FitsInNumType(_, ty, _) => ty.replace(name, replace_with), + Constraint::CanCastTo(_, ty1, ty2) => { + ty1.replace(name, replace_with) || ty2.replace(name, replace_with) + } + Constraint::TypeHasField(_, ty1, _, ty2) => { + ty1.replace(name, replace_with) || ty2.replace(name, replace_with) + } + Constraint::ConstantNumericType(_, ty) => ty.replace(name, replace_with), + Constraint::Equivalent(_, ty1, ty2) => { + ty1.replace(name, replace_with) || ty2.replace(name, replace_with) + } + Constraint::IsSigned(_, ty) => ty.replace(name, replace_with), + Constraint::IsSomething(_, ty) => ty.replace(name, replace_with), + Constraint::NumericType(_, ty) => ty.replace(name, replace_with), + Constraint::NamedTypeIs(_, name, ty) => ty.replace(name, replace_with), + } + } +} diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index a8fe5c0..8266eee 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -1,472 +1,541 @@ +use super::constraint::Constraint; +use super::InferenceEngine; use crate::eval::PrimitiveType; use crate::ir; use crate::syntax::{self, ConstantType}; -use crate::type_infer::solve::Constraint; use crate::util::scoped_map::ScopedMap; use internment::ArcIntern; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::str::FromStr; -enum TopLevelItem { - Type(ArcIntern, ir::TypeOrVar), - Value(ir::TopLevel), +struct ExpressionInfo { + expression: ir::Expression, + result_type: ir::TypeOrVar, + free_variables: HashSet>, + bound_variables: HashSet>, } -/// This function takes a syntactic program and converts it into the IR version of the -/// program, with appropriate type variables introduced and their constraints added to -/// the given database. -/// -/// If the input function has been validated (which it should be), then this should run -/// into no error conditions. However, if you failed to validate the input, then this -/// function can panic. -pub fn convert_program( - mut program: syntax::Program, -) -> (ir::Program, Vec) { - let mut constraint_db = Vec::new(); - let mut items = Vec::new(); - let mut renames = ScopedMap::new(); - let mut bindings = HashMap::new(); - let mut type_definitions = HashMap::new(); +impl ExpressionInfo { + fn simple(expression: ir::Expression, result_type: ir::TypeOrVar) -> Self { + ExpressionInfo { + expression, + result_type, + free_variables: HashSet::new(), + bound_variables: HashSet::new(), + } + } +} - for item in program.items.drain(..) { - let tli = convert_top_level(item, &mut constraint_db, &mut renames, &mut bindings); +impl InferenceEngine { + /// This function takes a syntactic program and converts it into the IR version of the + /// program, with appropriate type variables introduced and their constraints added to + /// the given database. + /// + /// If the input function has been validated (which it should be), then this should run + /// into no error conditions. However, if you failed to validate the input, then this + /// function can panic. + pub fn injest_program(&mut self, program: syntax::Program) { + let mut renames = ScopedMap::new(); - match tli { - TopLevelItem::Value(item) => items.push(item), - TopLevelItem::Type(name, decl) => { - let _ = type_definitions.insert(name, decl); + for item in program.items.into_iter() { + self.convert_top_level(item, &mut renames); + } + } + + /// This function takes a top-level item and converts it into the IR version of the + /// program, with all the appropriate type variables introduced and their constraints + /// added to the given database. + fn convert_top_level( + &mut self, + top_level: syntax::TopLevel, + renames: &mut ScopedMap, ArcIntern>, + ) { + match top_level { + syntax::TopLevel::Expression(expr) => { + let expr_info = self.convert_expression(expr, renames); + self.statements.push(expr_info.expression); + } + + syntax::TopLevel::Structure(_loc, name, fields) => { + let mut updated_fields = ir::Fields::default(); + + for (name, field_type) in fields.into_iter() { + updated_fields.insert(name.intern(), self.convert_type(field_type)); + } + + self.type_definitions + .insert(name.intern(), ir::TypeOrVar::Structure(updated_fields)); } } } - ( - ir::Program { - items, - type_definitions, - }, - constraint_db, - ) -} + /// This function takes a syntactic expression and converts it into a series + /// of IR statements, adding type variables and constraints as necessary. + /// + /// We generate a series of statements because we're going to flatten all + /// incoming expressions so that they are no longer recursive. This will + /// generate a bunch of new bindings for all the subexpressions, which we + /// return as a bundle. + /// + /// See the safety warning on [`convert_program`]! This function assumes that + /// you have run [`Statement::validate`], and will trigger panics in error + /// conditions if you have run that and had it come back clean. + fn convert_expression( + &mut self, + expression: syntax::Expression, + renames: &mut ScopedMap, ArcIntern>, + ) -> ExpressionInfo { + match expression { + // converting values is mostly tedious, because there's so many cases + // involved + syntax::Expression::Value(loc, val) => match val { + syntax::Value::Number(base, mctype, value) => { + let (newval, newtype) = match mctype { + None => { + let newtype = ir::TypeOrVar::new(); + let newval = ir::Value::U64(base, value); -/// This function takes a top-level item and converts it into the IR version of the -/// program, with all the appropriate type variables introduced and their constraints -/// added to the given database. -fn convert_top_level( - top_level: syntax::TopLevel, - constraint_db: &mut Vec, - renames: &mut ScopedMap, ArcIntern>, - bindings: &mut HashMap, ir::TypeOrVar>, -) -> TopLevelItem { - match top_level { - syntax::TopLevel::Function(name, args, _, expr) => { - // First, at some point we're going to want to know a location for this function, - // which should either be the name if we have one, or the body if we don't. - let function_location = match name { - None => expr.location().clone(), - Some(ref name) => name.location.clone(), - }; - // Next, let us figure out what we're going to name this function. If the user - // didn't provide one, we'll just call it "function:" for them. (We'll - // want a name for this function, eventually, so we might as well do it now.) - // - // If they did provide a name, see if we're shadowed. IF we are, then we'll have - // to specialize the name a bit. Otherwise we'll stick with their name. - let function_name = match name { - None => ir::gensym("function"), - Some(unbound) => finalize_name(bindings, renames, unbound), - }; + self.constraints.push(Constraint::ConstantNumericType( + loc.clone(), + newtype.clone(), + )); + (newval, newtype) + } + Some(ConstantType::Void) => ( + ir::Value::Void, + ir::TypeOrVar::Primitive(PrimitiveType::Void), + ), + Some(ConstantType::U8) => ( + ir::Value::U8(base, value as u8), + ir::TypeOrVar::Primitive(PrimitiveType::U8), + ), + Some(ConstantType::U16) => ( + ir::Value::U16(base, value as u16), + ir::TypeOrVar::Primitive(PrimitiveType::U16), + ), + Some(ConstantType::U32) => ( + ir::Value::U32(base, value as u32), + ir::TypeOrVar::Primitive(PrimitiveType::U32), + ), + Some(ConstantType::U64) => ( + ir::Value::U64(base, value), + ir::TypeOrVar::Primitive(PrimitiveType::U64), + ), + Some(ConstantType::I8) => ( + ir::Value::I8(base, value as i8), + ir::TypeOrVar::Primitive(PrimitiveType::I8), + ), + Some(ConstantType::I16) => ( + ir::Value::I16(base, value as i16), + ir::TypeOrVar::Primitive(PrimitiveType::I16), + ), + Some(ConstantType::I32) => ( + ir::Value::I32(base, value as i32), + ir::TypeOrVar::Primitive(PrimitiveType::I32), + ), + Some(ConstantType::I64) => ( + ir::Value::I64(base, value as i64), + ir::TypeOrVar::Primitive(PrimitiveType::I64), + ), + }; - // This function is going to have a type. We don't know what it is, but it'll have - // one. - let function_type = ir::TypeOrVar::new(); - bindings.insert(function_name.clone(), function_type.clone()); - - // Then, let's figure out what to do with the argument names, which similarly - // may need to be renamed. We'll also generate some new type variables to associate - // with all of them. - // - // Note that we want to do all this in a new renaming scope, so that we shadow - // appropriately. - renames.new_scope(); - let arginfo = args - .into_iter() - .map(|(name, mut declared_type)| { - let new_type = ir::TypeOrVar::new(); - constraint_db.push(Constraint::IsSomething( - name.location.clone(), - new_type.clone(), + self.constraints.push(Constraint::FitsInNumType( + loc.clone(), + newtype.clone(), + value, )); - let new_name = finalize_name(bindings, renames, name.clone()); - bindings.insert(new_name.clone(), new_type.clone()); - if let Some(declared_type) = declared_type.take() { - let declared_type = convert_type(declared_type, constraint_db); - constraint_db.push(Constraint::Equivalent( - name.location.clone(), - new_type.clone(), - declared_type, - )); - } + ExpressionInfo::simple( + ir::Expression::Atomic(ir::ValueOrRef::Value(loc, newtype.clone(), newval)), + newtype, + ) + } + }, - (new_name, new_type) - }) - .collect::>(); + syntax::Expression::Constructor(loc, name, fields) => { + let mut result_fields = HashMap::new(); + let mut type_fields = ir::Fields::default(); + let mut prereqs = vec![]; + let mut free_variables = HashSet::new(); + let mut bound_variables = HashSet::new(); - // Now we manufacture types for the outputs and then a type for the function itself. - // We're not going to make any claims on these types, yet; they're all just unknown - // type variables we need to work out. - let rettype = ir::TypeOrVar::new(); - let actual_function_type = ir::TypeOrVar::Function( - arginfo.iter().map(|x| x.1.clone()).collect(), - Box::new(rettype.clone()), - ); - constraint_db.push(Constraint::Equivalent( - function_location, - function_type, - actual_function_type, - )); + for (name, syntax_expr) in fields.into_iter() { + let field_expr_info = self.convert_expression(syntax_expr, renames); + type_fields.insert(name.clone().intern(), field_expr_info.result_type); + let (prereq, value) = simplify_expr(field_expr_info.expression); + result_fields.insert(name.clone().intern(), value); + merge_prereq(&mut prereqs, prereq); + free_variables.extend(field_expr_info.free_variables); + bound_variables.extend(field_expr_info.bound_variables); + } - // Now let's convert the body over to the new IR. - let (expr, ty) = convert_expression(expr, constraint_db, renames, bindings); - constraint_db.push(Constraint::Equivalent( - expr.location().clone(), - rettype.clone(), - ty, - )); + let result_type = ir::TypeOrVar::Structure(type_fields); - // Remember to exit this scoping level! - renames.release_scope(); + self.constraints.push(Constraint::NamedTypeIs( + loc.clone(), + name.clone().intern(), + result_type.clone(), + )); + let expression = ir::Expression::Construct( + loc, + result_type.clone(), + name.intern(), + result_fields, + ); - TopLevelItem::Value(ir::TopLevel::Function( - function_name, - arginfo, - rettype, - expr, - )) - } - - syntax::TopLevel::Expression(expr) => TopLevelItem::Value(ir::TopLevel::Statement( - convert_expression(expr, constraint_db, renames, bindings).0, - )), - - syntax::TopLevel::Structure(_loc, name, fields) => { - let mut updated_fields = ir::Fields::default(); - - for (name, field_type) in fields.into_iter() { - updated_fields.insert(name.intern(), convert_type(field_type, constraint_db)); + ExpressionInfo { + expression, + result_type, + free_variables, + bound_variables, + } } - TopLevelItem::Type(name.intern(), ir::TypeOrVar::Structure(updated_fields)) - } - } -} + syntax::Expression::Reference(loc, name) => { + let iname = ArcIntern::new(name); + let final_name = renames.get(&iname).cloned().unwrap_or(iname); + let result_type = self + .variable_types + .get(&final_name) + .cloned() + .expect("variable bound before use"); + let expression = ir::Expression::Atomic(ir::ValueOrRef::Ref( + loc, + result_type.clone(), + final_name.clone(), + )); + let free_variables = HashSet::from([final_name]); -/// This function takes a syntactic expression and converts it into a series -/// of IR statements, adding type variables and constraints as necessary. -/// -/// We generate a series of statements because we're going to flatten all -/// incoming expressions so that they are no longer recursive. This will -/// generate a bunch of new bindings for all the subexpressions, which we -/// return as a bundle. -/// -/// See the safety warning on [`convert_program`]! This function assumes that -/// you have run [`Statement::validate`], and will trigger panics in error -/// conditions if you have run that and had it come back clean. -fn convert_expression( - expression: syntax::Expression, - constraint_db: &mut Vec, - renames: &mut ScopedMap, ArcIntern>, - bindings: &mut HashMap, ir::TypeOrVar>, -) -> (ir::Expression, ir::TypeOrVar) { - match expression { - // converting values is mostly tedious, because there's so many cases - // involved - syntax::Expression::Value(loc, val) => match val { - syntax::Value::Number(base, mctype, value) => { - let (newval, newtype) = match mctype { - None => { - let newtype = ir::TypeOrVar::new(); - let newval = ir::Value::U64(base, value); + ExpressionInfo { + expression, + result_type, + free_variables, + bound_variables: HashSet::new(), + } + } - constraint_db.push(Constraint::ConstantNumericType( - loc.clone(), - newtype.clone(), - )); - (newval, newtype) + syntax::Expression::FieldRef(loc, expr, field) => { + let mut expr_info = self.convert_expression(*expr, renames); + let (prereqs, val_or_ref) = simplify_expr(expr_info.expression); + let result_type = ir::TypeOrVar::new(); + let result = ir::Expression::FieldRef( + loc.clone(), + result_type.clone(), + expr_info.result_type.clone(), + val_or_ref, + field.clone().intern(), + ); + + self.constraints.push(Constraint::TypeHasField( + loc, + expr_info.result_type.clone(), + field.intern(), + result_type.clone(), + )); + + expr_info.expression = finalize_expression(prereqs, result); + expr_info.result_type = result_type; + + expr_info + } + + syntax::Expression::Cast(loc, target, expr) => { + let mut expr_info = self.convert_expression(*expr, renames); + let (prereqs, val_or_ref) = simplify_expr(expr_info.expression); + let target_type: ir::TypeOrVar = PrimitiveType::from_str(&target) + .expect("valid type for cast") + .into(); + let res = ir::Expression::Cast(loc.clone(), target_type.clone(), val_or_ref); + + self.constraints.push(Constraint::CanCastTo( + loc, + expr_info.result_type.clone(), + target_type.clone(), + )); + + expr_info.expression = finalize_expression(prereqs, res); + expr_info.result_type = target_type; + + expr_info + } + + syntax::Expression::Primitive(loc, name) => { + let primop = ir::Primitive::from_str(&name.name).expect("valid primitive"); + + match primop { + ir::Primitive::Plus | ir::Primitive::Times | ir::Primitive::Divide => { + let numeric_type = ir::TypeOrVar::new_located(loc.clone()); + self.constraints + .push(Constraint::NumericType(loc.clone(), numeric_type.clone())); + let funtype = ir::TypeOrVar::Function( + vec![numeric_type.clone(), numeric_type.clone()], + Box::new(numeric_type.clone()), + ); + let result_value = ir::ValueOrRef::Primitive(loc, funtype.clone(), primop); + ExpressionInfo::simple(ir::Expression::Atomic(result_value), funtype) } - Some(ConstantType::Void) => ( - ir::Value::Void, - ir::TypeOrVar::Primitive(PrimitiveType::Void), - ), - Some(ConstantType::U8) => ( - ir::Value::U8(base, value as u8), - ir::TypeOrVar::Primitive(PrimitiveType::U8), - ), - Some(ConstantType::U16) => ( - ir::Value::U16(base, value as u16), - ir::TypeOrVar::Primitive(PrimitiveType::U16), - ), - Some(ConstantType::U32) => ( - ir::Value::U32(base, value as u32), - ir::TypeOrVar::Primitive(PrimitiveType::U32), - ), - Some(ConstantType::U64) => ( - ir::Value::U64(base, value), - ir::TypeOrVar::Primitive(PrimitiveType::U64), - ), - Some(ConstantType::I8) => ( - ir::Value::I8(base, value as i8), - ir::TypeOrVar::Primitive(PrimitiveType::I8), - ), - Some(ConstantType::I16) => ( - ir::Value::I16(base, value as i16), - ir::TypeOrVar::Primitive(PrimitiveType::I16), - ), - Some(ConstantType::I32) => ( - ir::Value::I32(base, value as i32), - ir::TypeOrVar::Primitive(PrimitiveType::I32), - ), - Some(ConstantType::I64) => ( - ir::Value::I64(base, value as i64), - ir::TypeOrVar::Primitive(PrimitiveType::I64), - ), + + ir::Primitive::Minus => { + let numeric_type = ir::TypeOrVar::new_located(loc.clone()); + self.constraints + .push(Constraint::NumericType(loc.clone(), numeric_type.clone())); + let funtype = ir::TypeOrVar::Function( + vec![numeric_type.clone(), numeric_type.clone()], + Box::new(numeric_type.clone()), + ); + let result_value = ir::ValueOrRef::Primitive(loc, funtype.clone(), primop); + ExpressionInfo::simple(ir::Expression::Atomic(result_value), funtype) + } + + ir::Primitive::Print => { + let arg_type = ir::TypeOrVar::new_located(loc.clone()); + self.constraints + .push(Constraint::Printable(loc.clone(), arg_type.clone())); + let funtype = ir::TypeOrVar::Function( + vec![arg_type], + Box::new(ir::TypeOrVar::Primitive(PrimitiveType::Void)), + ); + let result_value = ir::ValueOrRef::Primitive(loc, funtype.clone(), primop); + ExpressionInfo::simple(ir::Expression::Atomic(result_value), funtype) + } + + ir::Primitive::Negate => { + let arg_type = ir::TypeOrVar::new_located(loc.clone()); + self.constraints + .push(Constraint::NumericType(loc.clone(), arg_type.clone())); + self.constraints + .push(Constraint::IsSigned(loc.clone(), arg_type.clone())); + let funtype = + ir::TypeOrVar::Function(vec![arg_type.clone()], Box::new(arg_type)); + let result_value = ir::ValueOrRef::Primitive(loc, funtype.clone(), primop); + ExpressionInfo::simple(ir::Expression::Atomic(result_value), funtype) + } + } + } + + syntax::Expression::Call(loc, fun, args) => { + let return_type = ir::TypeOrVar::new(); + let arg_types = args + .iter() + .map(|_| ir::TypeOrVar::new()) + .collect::>(); + + let mut expr_info = self.convert_expression(*fun, renames); + let target_fun_type = + ir::TypeOrVar::Function(arg_types.clone(), Box::new(return_type.clone())); + self.constraints.push(Constraint::Equivalent( + loc.clone(), + expr_info.result_type, + target_fun_type, + )); + let mut prereqs = vec![]; + + let (fun_prereqs, fun) = simplify_expr(expr_info.expression); + merge_prereq(&mut prereqs, fun_prereqs); + + let new_args = args + .into_iter() + .zip(arg_types) + .map(|(arg, target_type)| { + let arg_info = self.convert_expression(arg, renames); + let location = arg_info.expression.location().clone(); + let (arg_prereq, new_valref) = simplify_expr(arg_info.expression); + merge_prereq(&mut prereqs, arg_prereq); + self.constraints.push(Constraint::Equivalent( + location, + arg_info.result_type, + target_type, + )); + expr_info.free_variables.extend(arg_info.free_variables); + expr_info.bound_variables.extend(arg_info.bound_variables); + new_valref + }) + .collect(); + + let last_call = + ir::Expression::Call(loc.clone(), return_type.clone(), fun, new_args); + + expr_info.expression = finalize_expressions(prereqs, last_call); + expr_info.result_type = return_type; + + expr_info + } + + syntax::Expression::Block(loc, stmts) => { + let mut result_type = ir::TypeOrVar::Primitive(PrimitiveType::Void); + let mut exprs = vec![]; + let mut free_variables = HashSet::new(); + let mut bound_variables = HashSet::new(); + + for xpr in stmts.into_iter() { + let expr_info = self.convert_expression(xpr, renames); + result_type = expr_info.result_type; + exprs.push(expr_info.expression); + free_variables.extend( + expr_info + .free_variables + .difference(&bound_variables) + .cloned() + .collect::>(), + ); + bound_variables.extend(expr_info.bound_variables); + } + + ExpressionInfo { + expression: ir::Expression::Block(loc, result_type.clone(), exprs), + result_type, + free_variables, + bound_variables, + } + } + + syntax::Expression::Binding(loc, name, expr) => { + let mut expr_info = self.convert_expression(*expr, renames); + let final_name = self.finalize_name(renames, name); + self.variable_types + .insert(final_name.clone(), expr_info.result_type.clone()); + expr_info.expression = ir::Expression::Bind( + loc, + final_name.clone(), + expr_info.result_type.clone(), + Box::new(expr_info.expression), + ); + expr_info.bound_variables.insert(final_name); + expr_info + } + + syntax::Expression::Function(_, name, args, _, expr) => { + // First, at some point we're going to want to know a location for this function, + // which should either be the name if we have one, or the body if we don't. + let function_location = match name { + None => expr.location().clone(), + Some(ref name) => name.location.clone(), + }; + // Next, let us figure out what we're going to name this function. If the user + // didn't provide one, we'll just call it "function:" for them. (We'll + // want a name for this function, eventually, so we might as well do it now.) + // + // If they did provide a name, see if we're shadowed. IF we are, then we'll have + // to specialize the name a bit. Otherwise we'll stick with their name. + let function_name = match name { + None => ir::gensym("function"), + Some(unbound) => self.finalize_name(renames, unbound), }; - constraint_db.push(Constraint::FitsInNumType( - loc.clone(), - newtype.clone(), - value, + // This function is going to have a type. We don't know what it is, but it'll have + // one. + let function_type = ir::TypeOrVar::new(); + self.variable_types + .insert(function_name.clone(), function_type.clone()); + + // Then, let's figure out what to do with the argument names, which similarly + // may need to be renamed. We'll also generate some new type variables to associate + // with all of them. + // + // Note that we want to do all this in a new renaming scope, so that we shadow + // appropriately. + renames.new_scope(); + let arginfo = args + .into_iter() + .map(|(name, mut declared_type)| { + let new_type = ir::TypeOrVar::new(); + self.constraints.push(Constraint::IsSomething( + name.location.clone(), + new_type.clone(), + )); + let new_name = self.finalize_name(renames, name.clone()); + self.variable_types + .insert(new_name.clone(), new_type.clone()); + + if let Some(declared_type) = declared_type.take() { + let declared_type = self.convert_type(declared_type); + self.constraints.push(Constraint::Equivalent( + name.location.clone(), + new_type.clone(), + declared_type, + )); + } + + (new_name, new_type) + }) + .collect::>(); + + // Now we manufacture types for the outputs and then a type for the function itself. + // We're not going to make any claims on these types, yet; they're all just unknown + // type variables we need to work out. + let rettype = ir::TypeOrVar::new(); + let actual_function_type = ir::TypeOrVar::Function( + arginfo.iter().map(|x| x.1.clone()).collect(), + Box::new(rettype.clone()), + ); + self.constraints.push(Constraint::Equivalent( + function_location, + function_type, + actual_function_type, )); - ( - ir::Expression::Atomic(ir::ValueOrRef::Value(loc, newtype.clone(), newval)), - newtype, - ) + + // Now let's convert the body over to the new IR. + let expr_info = self.convert_expression(*expr, renames); + self.constraints.push(Constraint::Equivalent( + expr_info.expression.location().clone(), + rettype.clone(), + expr_info.result_type.clone(), + )); + + // Remember to exit this scoping level! + renames.release_scope(); + + self.functions + .insert(function_name, (arginfo, expr_info.expression.clone())); + + unimplemented!() } - }, - - syntax::Expression::Constructor(loc, name, fields) => { - let mut result_fields = HashMap::new(); - let mut type_fields = ir::Fields::default(); - let mut prereqs = vec![]; - - for (name, syntax_expr) in fields.into_iter() { - let (ir_expr, expr_type) = - convert_expression(syntax_expr, constraint_db, renames, bindings); - type_fields.insert(name.clone().intern(), expr_type); - let (prereq, value) = simplify_expr(ir_expr); - result_fields.insert(name.clone().intern(), value); - merge_prereq(&mut prereqs, prereq); - } - - let result_type = ir::TypeOrVar::Structure(type_fields); - - constraint_db.push(Constraint::NamedTypeIs( - loc.clone(), - name.clone().intern(), - result_type.clone(), - )); - let result = - ir::Expression::Construct(loc, result_type.clone(), name.intern(), result_fields); - - (finalize_expressions(prereqs, result), result_type) - } - - syntax::Expression::Reference(loc, name) => { - let iname = ArcIntern::new(name); - let final_name = renames.get(&iname).cloned().unwrap_or(iname); - let rtype = bindings - .get(&final_name) - .cloned() - .expect("variable bound before use"); - let refexp = - ir::Expression::Atomic(ir::ValueOrRef::Ref(loc, rtype.clone(), final_name)); - - (refexp, rtype) - } - - syntax::Expression::FieldRef(loc, expr, field) => { - let (nexpr, etype) = convert_expression(*expr, constraint_db, renames, bindings); - let (prereqs, val_or_ref) = simplify_expr(nexpr); - let result_type = ir::TypeOrVar::new(); - let result = ir::Expression::FieldRef( - loc.clone(), - result_type.clone(), - etype.clone(), - val_or_ref, - field.clone().intern(), - ); - - constraint_db.push(Constraint::TypeHasField( - loc, - etype, - field.intern(), - result_type.clone(), - )); - - (finalize_expression(prereqs, result), result_type) - } - - syntax::Expression::Cast(loc, target, expr) => { - let (nexpr, etype) = convert_expression(*expr, constraint_db, renames, bindings); - let (prereqs, val_or_ref) = simplify_expr(nexpr); - let target_type: ir::TypeOrVar = PrimitiveType::from_str(&target) - .expect("valid type for cast") - .into(); - let res = ir::Expression::Cast(loc.clone(), target_type.clone(), val_or_ref); - - constraint_db.push(Constraint::CanCastTo(loc, etype, target_type.clone())); - - (finalize_expression(prereqs, res), target_type) - } - - syntax::Expression::Primitive(loc, name) => { - let primop = ir::Primitive::from_str(&name.name).expect("valid primitive"); - - match primop { - ir::Primitive::Plus | ir::Primitive::Times | ir::Primitive::Divide => { - let numeric_type = ir::TypeOrVar::new_located(loc.clone()); - constraint_db.push(Constraint::NumericType(loc.clone(), numeric_type.clone())); - let funtype = ir::TypeOrVar::Function( - vec![numeric_type.clone(), numeric_type.clone()], - Box::new(numeric_type.clone()), - ); - let result_value = ir::ValueOrRef::Primitive(loc, funtype.clone(), primop); - (ir::Expression::Atomic(result_value), funtype) - } - - ir::Primitive::Minus => { - let numeric_type = ir::TypeOrVar::new_located(loc.clone()); - constraint_db.push(Constraint::NumericType(loc.clone(), numeric_type.clone())); - let funtype = ir::TypeOrVar::Function( - vec![numeric_type.clone(), numeric_type.clone()], - Box::new(numeric_type.clone()), - ); - let result_value = ir::ValueOrRef::Primitive(loc, funtype.clone(), primop); - (ir::Expression::Atomic(result_value), funtype) - } - - ir::Primitive::Print => { - let arg_type = ir::TypeOrVar::new_located(loc.clone()); - constraint_db.push(Constraint::Printable(loc.clone(), arg_type.clone())); - let funtype = ir::TypeOrVar::Function( - vec![arg_type], - Box::new(ir::TypeOrVar::Primitive(PrimitiveType::Void)), - ); - let result_value = ir::ValueOrRef::Primitive(loc, funtype.clone(), primop); - (ir::Expression::Atomic(result_value), funtype) - } - - ir::Primitive::Negate => { - let arg_type = ir::TypeOrVar::new_located(loc.clone()); - constraint_db.push(Constraint::NumericType(loc.clone(), arg_type.clone())); - constraint_db.push(Constraint::IsSigned(loc.clone(), arg_type.clone())); - let funtype = - ir::TypeOrVar::Function(vec![arg_type.clone()], Box::new(arg_type)); - let result_value = ir::ValueOrRef::Primitive(loc, funtype.clone(), primop); - (ir::Expression::Atomic(result_value), funtype) - } - } - } - - syntax::Expression::Call(loc, fun, args) => { - let return_type = ir::TypeOrVar::new(); - let arg_types = args - .iter() - .map(|_| ir::TypeOrVar::new()) - .collect::>(); - - let (new_fun, new_fun_type) = - convert_expression(*fun, constraint_db, renames, bindings); - let target_fun_type = - ir::TypeOrVar::Function(arg_types.clone(), Box::new(return_type.clone())); - constraint_db.push(Constraint::Equivalent( - loc.clone(), - new_fun_type, - target_fun_type, - )); - let mut prereqs = vec![]; - - let (fun_prereqs, fun) = simplify_expr(new_fun); - merge_prereq(&mut prereqs, fun_prereqs); - - let new_args = args - .into_iter() - .zip(arg_types) - .map(|(arg, target_type)| { - let (new_arg, inferred_type) = - convert_expression(arg, constraint_db, renames, bindings); - let location = new_arg.location().clone(); - let (arg_prereq, new_valref) = simplify_expr(new_arg); - merge_prereq(&mut prereqs, arg_prereq); - constraint_db.push(Constraint::Equivalent( - location, - inferred_type, - target_type, - )); - new_valref - }) - .collect(); - - let last_call = ir::Expression::Call(loc.clone(), return_type.clone(), fun, new_args); - - (finalize_expressions(prereqs, last_call), return_type) - } - - syntax::Expression::Block(loc, stmts) => { - let mut ret_type = ir::TypeOrVar::Primitive(PrimitiveType::Void); - let mut exprs = vec![]; - - for xpr in stmts.into_iter() { - let (expr, expr_type) = convert_expression(xpr, constraint_db, renames, bindings); - - ret_type = expr_type; - exprs.push(expr); - } - - ( - ir::Expression::Block(loc, ret_type.clone(), exprs), - ret_type, - ) - } - - syntax::Expression::Binding(loc, name, expr) => { - let (expr, ty) = convert_expression(*expr, constraint_db, renames, bindings); - let final_name = finalize_name(bindings, renames, name); - bindings.insert(final_name.clone(), ty.clone()); - - ( - ir::Expression::Bind(loc, final_name, ty.clone(), Box::new(expr)), - ty, - ) } } -} -fn convert_type(ty: syntax::Type, constraint_db: &mut Vec) -> ir::TypeOrVar { - match ty { - syntax::Type::Named(x) => match PrimitiveType::from_str(x.name.as_str()) { - Err(_) => { - let retval = ir::TypeOrVar::new_located(x.location.clone()); - constraint_db.push(Constraint::NamedTypeIs( - x.location.clone(), - x.intern(), - retval.clone(), - )); - retval + fn convert_type(&mut self, ty: syntax::Type) -> ir::TypeOrVar { + match ty { + syntax::Type::Named(x) => match PrimitiveType::from_str(x.name.as_str()) { + Err(_) => { + let retval = ir::TypeOrVar::new_located(x.location.clone()); + self.constraints.push(Constraint::NamedTypeIs( + x.location.clone(), + x.intern(), + retval.clone(), + )); + retval + } + Ok(v) => ir::TypeOrVar::Primitive(v), + }, + syntax::Type::Struct(fields) => { + let mut new_fields = ir::Fields::default(); + + for (name, field_type) in fields.into_iter() { + let new_field_type = field_type + .map(|x| self.convert_type(x)) + .unwrap_or_else(ir::TypeOrVar::new); + new_fields.insert(name.intern(), new_field_type); + } + + ir::TypeOrVar::Structure(new_fields) } - Ok(v) => ir::TypeOrVar::Primitive(v), - }, - syntax::Type::Struct(fields) => { - let mut new_fields = ir::Fields::default(); + } + } - for (name, field_type) in fields.into_iter() { - let new_field_type = field_type - .map(|x| convert_type(x, constraint_db)) - .unwrap_or_else(ir::TypeOrVar::new); - new_fields.insert(name.intern(), new_field_type); - } - - ir::TypeOrVar::Structure(new_fields) + fn finalize_name( + &mut self, + renames: &mut ScopedMap, ArcIntern>, + name: syntax::Name, + ) -> ArcIntern { + if self + .variable_types + .contains_key(&ArcIntern::new(name.name.clone())) + { + let new_name = ir::gensym(&name.name); + renames.insert(ArcIntern::new(name.name.to_string()), new_name.clone()); + new_name + } else { + ArcIntern::new(name.to_string()) } } } @@ -520,20 +589,6 @@ fn finalize_expressions( } } -fn finalize_name( - bindings: &HashMap, ir::TypeOrVar>, - renames: &mut ScopedMap, ArcIntern>, - name: syntax::Name, -) -> ArcIntern { - if bindings.contains_key(&ArcIntern::new(name.name.clone())) { - let new_name = ir::gensym(&name.name); - renames.insert(ArcIntern::new(name.name.to_string()), new_name.clone()); - new_name - } else { - ArcIntern::new(name.to_string()) - } -} - fn merge_prereq(left: &mut Vec, prereq: Option) { if let Some(item) = prereq { left.push(item) diff --git a/src/type_infer/error.rs b/src/type_infer/error.rs new file mode 100644 index 0000000..dcd0a6c --- /dev/null +++ b/src/type_infer/error.rs @@ -0,0 +1,146 @@ +use super::constraint::Constraint; +use crate::eval::PrimitiveType; +use crate::ir::{Primitive, TypeOrVar}; +use crate::syntax::Location; +use codespan_reporting::diagnostic::Diagnostic; +use internment::ArcIntern; + +/// The various kinds of errors that can occur while doing type inference. +pub enum TypeInferenceError { + /// The user provide a constant that is too large for its inferred type. + ConstantTooLarge(Location, PrimitiveType, u64), + /// Somehow we're trying to use a non-number as a number + NotANumber(Location, PrimitiveType), + /// The two types needed to be equivalent, but weren't. + NotEquivalent(Location, TypeOrVar, TypeOrVar), + /// We cannot safely cast the first type to the second type. + CannotSafelyCast(Location, PrimitiveType, PrimitiveType), + /// The primitive invocation provided the wrong number of arguments. + WrongPrimitiveArity(Location, Primitive, usize, usize, usize), + /// We cannot cast between the type types, for any number of reasons + CannotCast(Location, TypeOrVar, TypeOrVar), + /// We cannot turn a number into a function. + CannotMakeNumberAFunction(Location, TypeOrVar, Option), + /// We cannot turn a number into a Structure. + CannotMakeNumberAStructure(Location, TypeOrVar, Option), + /// We had a constraint we just couldn't solve. + CouldNotSolve(Constraint), + /// Functions are not printable. + FunctionsAreNotPrintable(Location), + /// The given type isn't signed, and can't be negated + IsNotSigned(Location, TypeOrVar), + /// The given type doesn't have the given field. + NoFieldForType(Location, ArcIntern, TypeOrVar), + /// There is no type with the given name. + UnknownTypeName(Location, ArcIntern), +} + +impl From for Diagnostic { + fn from(value: TypeInferenceError) -> Self { + match value { + TypeInferenceError::ConstantTooLarge(loc, primty, value) => loc + .labelled_error("constant too large for type") + .with_message(format!( + "Type {} has a max value of {}, which is smaller than {}", + primty, + primty.max_value().expect("constant type has max value"), + value + )), + TypeInferenceError::NotANumber(loc, primty) => loc + .labelled_error("not a numeric type") + .with_message(format!( + "For some reason, we're trying to use {} as a numeric type", + primty, + )), + TypeInferenceError::NotEquivalent(loc, ty1, ty2) => loc + .labelled_error("type inference error") + .with_message(format!("Expected type {}, received type {}", ty1, ty2)), + TypeInferenceError::CannotSafelyCast(loc, ty1, ty2) => loc + .labelled_error("unsafe type cast") + .with_message(format!("Cannot safely cast {} to {}", ty1, ty2)), + TypeInferenceError::WrongPrimitiveArity(loc, prim, lower, upper, observed) => loc + .labelled_error("wrong number of arguments") + .with_message(format!( + "expected {} for {}, received {}", + if lower == upper && lower > 1 { + format!("{} arguments", lower) + } else if lower == upper { + format!("{} argument", lower) + } else { + format!("{}-{} arguments", lower, upper) + }, + prim, + observed + )), + TypeInferenceError::CannotCast(loc, t1, t2) => loc + .labelled_error("cannot cast between types") + .with_message(format!( + "tried to cast from {} to {}", + t1, t2, + )), + TypeInferenceError::CannotMakeNumberAFunction(loc, t, val) => loc + .labelled_error(if let Some(val) = val { + format!("cannot turn {} into a function", val) + } else { + "cannot use a constant as a function type".to_string() + }) + .with_message(format!("function type was {}", t)), + TypeInferenceError::CannotMakeNumberAStructure(loc, t, val) => loc + .labelled_error(if let Some(val) = val { + format!("cannot turn {} into a function", val) + } else { + "cannot use a constant as a function type".to_string() + }) + .with_message(format!("function type was {}", t)), + TypeInferenceError::FunctionsAreNotPrintable(loc) => loc + .labelled_error("cannot print function values"), + TypeInferenceError::IsNotSigned(loc, pt) => loc + .labelled_error(format!("type {} is not signed", pt)) + .with_message("and so it cannot be negated"), + TypeInferenceError::NoFieldForType(loc, field, t) => loc + .labelled_error(format!("no field {} available for type {}", field, t)), + TypeInferenceError::UnknownTypeName(loc , name) => loc + .labelled_error(format!("unknown type named {}", name)), + TypeInferenceError::CouldNotSolve(Constraint::CanCastTo(loc, a, b)) => { + loc.labelled_error("internal error").with_message(format!( + "could not determine if it was safe to cast from {} to {}", + a, b + )) + } + TypeInferenceError::CouldNotSolve(Constraint::TypeHasField(loc, a, field, _)) => { + loc.labelled_error("internal error") + .with_message(format!("fould not determine if type {} has field {}", a, field)) + } + TypeInferenceError::CouldNotSolve(Constraint::Equivalent(loc, a, b)) => { + loc.labelled_error("internal error").with_message(format!( + "could not determine if {} and {} were equivalent", + a, b + )) + } + TypeInferenceError::CouldNotSolve(Constraint::FitsInNumType(loc, ty, val)) => { + loc.labelled_error("internal error").with_message(format!( + "Could not determine if {} could fit in {}", + val, ty + )) + } + TypeInferenceError::CouldNotSolve(Constraint::NumericType(loc, ty)) => loc + .labelled_error("internal error") + .with_message(format!("Could not determine if {} was a numeric type", ty)), + TypeInferenceError::CouldNotSolve(Constraint::ConstantNumericType(loc, ty)) => + panic!("What? Constants should always eventually be solved, even by default; {:?} and type {:?}", loc, ty), + TypeInferenceError::CouldNotSolve(Constraint::Printable(loc, ty)) => loc + .labelled_error("internal error") + .with_message(format!("Could not determine if type {} was printable", ty)), + TypeInferenceError::CouldNotSolve(Constraint::IsSomething(loc, _)) => { + loc.labelled_error("could not infer type") + .with_message("Could not find *any* type information; is this an unused function argument?") + } + TypeInferenceError::CouldNotSolve(Constraint::IsSigned(loc, t)) => loc + .labelled_error("internal error") + .with_message(format!("could not infer that type {} was signed", t)), + TypeInferenceError::CouldNotSolve(Constraint::NamedTypeIs(loc, name, ty)) => loc + .labelled_error("internal error") + .with_message(format!("could not infer that the name {} refers to {}", name, ty)), + } + } +} diff --git a/src/type_infer/finalize.rs b/src/type_infer/finalize.rs index 624bbe8..9265953 100644 --- a/src/type_infer/finalize.rs +++ b/src/type_infer/finalize.rs @@ -1,41 +1,61 @@ -use super::solve::TypeResolutions; use crate::eval::PrimitiveType; -use crate::ir::{Expression, Program, TopLevel, Type, TypeOrVar, Value, ValueOrRef}; +use crate::ir::{Expression, Program, TopLevel, Type, TypeOrVar, TypeWithVoid, Value, ValueOrRef}; +use crate::syntax::Location; +use internment::ArcIntern; +use std::collections::HashMap; -pub fn finalize_program( - program: Program, - resolutions: &TypeResolutions, -) -> Program { - for (name, ty) in resolutions.iter() { - tracing::debug!(name = %name, resolved_type = %ty, "resolved type variable"); - } +pub type TypeResolutions = HashMap, Type>; - Program { - items: program - .items - .into_iter() - .map(|x| finalize_top_level(x, resolutions)) - .collect(), +impl super::InferenceEngine { + pub fn finalize_program(self, resolutions: TypeResolutions) -> Program { + for (name, ty) in resolutions.iter() { + tracing::debug!(name = %name, resolved_type = %ty, "resolved type variable"); + } - type_definitions: program - .type_definitions - .into_iter() - .map(|(n, t)| (n, finalize_type(t, resolutions))) - .collect(), - } -} + let mut type_definitions = HashMap::new(); + let mut items = Vec::new(); -fn finalize_top_level(item: TopLevel, resolutions: &TypeResolutions) -> TopLevel { - match item { - TopLevel::Function(name, args, rettype, expr) => TopLevel::Function( - name, - args.into_iter() - .map(|(name, t)| (name, finalize_type(t, resolutions))) - .collect(), - finalize_type(rettype, resolutions), - finalize_expression(expr, resolutions), - ), - TopLevel::Statement(expr) => TopLevel::Statement(finalize_expression(expr, resolutions)), + for (name, def) in self.type_definitions.into_iter() { + type_definitions.insert(name, finalize_type(def, &resolutions)); + } + + for (name, (arguments, body)) in self.functions.into_iter() { + let new_body = finalize_expression(body, &resolutions); + let arguments = arguments + .into_iter() + .map(|(name, t)| (name, finalize_type(t, &resolutions))) + .collect(); + items.push(TopLevel::Function( + name, + arguments, + new_body.type_of(), + new_body, + )); + } + + let mut body = vec![]; + let mut last_type = Type::void(); + let mut location = None; + + for expr in self.statements.into_iter() { + let next = finalize_expression(expr, &resolutions); + location = location + .map(|x: Location| x.merge(next.location())) + .unwrap_or_else(|| Some(next.location().clone())); + last_type = next.type_of(); + body.push(next); + } + + items.push(TopLevel::Statement(Expression::Block( + location.unwrap_or_else(Location::manufactured), + last_type, + body, + ))); + + Program { + items, + type_definitions, + } } } diff --git a/src/type_infer/result.rs b/src/type_infer/result.rs new file mode 100644 index 0000000..49c474f --- /dev/null +++ b/src/type_infer/result.rs @@ -0,0 +1,50 @@ +use super::error::TypeInferenceError; +use super::warning::TypeInferenceWarning; + +/// The results of type inference; like [`Result`], but with a bit more information. +/// +/// This result is parameterized, because sometimes it's handy to return slightly +/// different things; there's a [`TypeInferenceResult::map`] function for performing +/// those sorts of conversions. +pub enum TypeInferenceResult { + Success { + result: Result, + warnings: Vec, + }, + Failure { + errors: Vec, + warnings: Vec, + }, +} + +impl TypeInferenceResult { + // If this was a successful type inference, run the function over the result to + // create a new result. + // + // This is the moral equivalent of [`Result::map`], but for type inference results. + pub fn map(self, f: F) -> TypeInferenceResult + where + F: FnOnce(R) -> U, + { + match self { + TypeInferenceResult::Success { result, warnings } => TypeInferenceResult::Success { + result: f(result), + warnings, + }, + + TypeInferenceResult::Failure { errors, warnings } => { + TypeInferenceResult::Failure { errors, warnings } + } + } + } + + // Return the final result, or panic if it's not a success + pub fn expect(self, msg: &str) -> R { + match self { + TypeInferenceResult::Success { result, .. } => result, + TypeInferenceResult::Failure { .. } => { + panic!("tried to get value from failed type inference: {}", msg) + } + } + } +} diff --git a/src/type_infer/solve.rs b/src/type_infer/solve.rs index 15dc067..74f1c9f 100644 --- a/src/type_infer/solve.rs +++ b/src/type_infer/solve.rs @@ -1,826 +1,556 @@ +use super::constraint::Constraint; +use super::error::TypeInferenceError; +use super::warning::TypeInferenceWarning; use crate::eval::PrimitiveType; -use crate::ir::{Primitive, Type, TypeOrVar}; -use crate::syntax::Location; -use codespan_reporting::diagnostic::Diagnostic; +use crate::ir::TypeOrVar; use internment::ArcIntern; -use std::{collections::HashMap, fmt}; -/// A type inference constraint that we're going to need to solve. -#[derive(Debug)] -pub enum Constraint { - /// The given type must be printable using the `print` built-in - Printable(Location, TypeOrVar), - /// The provided numeric value fits in the given constant type - FitsInNumType(Location, TypeOrVar, u64), - /// The given type can be casted to the target type safely - CanCastTo(Location, TypeOrVar, TypeOrVar), - /// The given type has the given field in it, and the type of that field - /// is as given. - TypeHasField(Location, TypeOrVar, ArcIntern, TypeOrVar), - /// The given type must be some numeric type, but this is not a constant - /// value, so don't try to default it if we can't figure it out - NumericType(Location, TypeOrVar), - /// The given type is attached to a constant and must be some numeric type. - /// If we can't figure it out, we should warn the user and then just use a - /// default. - ConstantNumericType(Location, TypeOrVar), - /// The two types should be equivalent - Equivalent(Location, TypeOrVar, TypeOrVar), - /// The given type can be resolved to something - IsSomething(Location, TypeOrVar), - /// The given type can be negated - IsSigned(Location, TypeOrVar), - /// Checks to see if the given named type is equivalent to the provided one. - NamedTypeIs(Location, ArcIntern, TypeOrVar), -} - -impl fmt::Display for Constraint { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Constraint::Printable(_, ty) => write!(f, "PRINTABLE {}", ty), - Constraint::FitsInNumType(_, ty, num) => write!(f, "FITS_IN {} {}", num, ty), - Constraint::CanCastTo(_, ty, ty2) => write!(f, "CAST {} -> {}", ty, ty2), - Constraint::TypeHasField(_, ty1, field, ty2) => { - write!(f, "FIELD {}.{} -> {}", ty1, field, ty2) - } - Constraint::NumericType(_, ty) => write!(f, "NUMERIC {}", ty), - Constraint::ConstantNumericType(_, ty) => write!(f, "CONST_NUMERIC {}", ty), - Constraint::Equivalent(_, ty, ty2) => write!(f, "EQUIVALENT {} => {}", ty, ty2), - Constraint::IsSomething(_, ty) => write!(f, "SOMETHING {}", ty), - Constraint::IsSigned(_, ty) => write!(f, "SIGNED {}", ty), - Constraint::NamedTypeIs(_, name, ty) => write!(f, "TYPE_EQUIV {} == {}", name, ty), - } - } -} - -impl Constraint { - /// Replace all instances of the name (anywhere! including on the left hand side of equivalences!) - /// with the given type. +impl super::InferenceEngine { + /// Solve all the constraints in the provided database. /// - /// Returns whether or not anything was changed in the constraint. - fn replace(&mut self, name: &ArcIntern, replace_with: &TypeOrVar) -> bool { - match self { - Constraint::Printable(_, ty) => ty.replace(name, replace_with), - Constraint::FitsInNumType(_, ty, _) => ty.replace(name, replace_with), - Constraint::CanCastTo(_, ty1, ty2) => { - ty1.replace(name, replace_with) || ty2.replace(name, replace_with) + /// This process can take a bit, so you might not want to do it multiple times. Basically, + /// it's going to grind on these constraints until either it figures them out, or it stops + /// making progress. I haven't done the math on the constraints to even figure out if this + /// is guaranteed to halt, though, let alone terminate in some reasonable amount of time. + /// + /// The return value is a type inference result, which pairs some warnings with either a + /// successful set of type resolutions (mappings from type variables to their values), or + /// a series of inference errors. + pub fn solve_constraints(&mut self) { + let mut iteration = 0u64; + + loop { + let mut changed_something = false; + let mut all_constraints_solved = true; + let mut new_constraints = vec![]; + + tracing::debug!(iteration, "Restarting constraint solving loop"); + for constraint in self.constraints.iter() { + tracing::debug!(%constraint, "remaining constraint"); } - Constraint::TypeHasField(_, ty1, _, ty2) => { - ty1.replace(name, replace_with) || ty2.replace(name, replace_with) - } - Constraint::ConstantNumericType(_, ty) => ty.replace(name, replace_with), - Constraint::Equivalent(_, ty1, ty2) => { - ty1.replace(name, replace_with) || ty2.replace(name, replace_with) - } - Constraint::IsSigned(_, ty) => ty.replace(name, replace_with), - Constraint::IsSomething(_, ty) => ty.replace(name, replace_with), - Constraint::NumericType(_, ty) => ty.replace(name, replace_with), - Constraint::NamedTypeIs(_, name, ty) => ty.replace(name, replace_with), - } - } -} + iteration += 1; -pub type TypeResolutions = HashMap, Type>; + while let Some(constraint) = self.constraints.pop() { + match constraint { + // The basic philosophy of this match block is that, for each constraint, we're + // going to start seeing if we can just solve (or abandon) the constraint. Then, + // if we can't, we'll just chuck it back on our list for later. -/// The results of type inference; like [`Result`], but with a bit more information. -/// -/// This result is parameterized, because sometimes it's handy to return slightly -/// different things; there's a [`TypeInferenceResult::map`] function for performing -/// those sorts of conversions. -pub enum TypeInferenceResult { - Success { - result: Result, - warnings: Vec, - }, - Failure { - errors: Vec, - warnings: Vec, - }, -} - -impl TypeInferenceResult { - // If this was a successful type inference, run the function over the result to - // create a new result. - // - // This is the moral equivalent of [`Result::map`], but for type inference results. - pub fn map(self, f: F) -> TypeInferenceResult - where - F: FnOnce(R) -> U, - { - match self { - TypeInferenceResult::Success { result, warnings } => TypeInferenceResult::Success { - result: f(result), - warnings, - }, - - TypeInferenceResult::Failure { errors, warnings } => { - TypeInferenceResult::Failure { errors, warnings } - } - } - } - - // Return the final result, or panic if it's not a success - pub fn expect(self, msg: &str) -> R { - match self { - TypeInferenceResult::Success { result, .. } => result, - TypeInferenceResult::Failure { .. } => { - panic!("tried to get value from failed type inference: {}", msg) - } - } - } -} - -/// The various kinds of errors that can occur while doing type inference. -pub enum TypeInferenceError { - /// The user provide a constant that is too large for its inferred type. - ConstantTooLarge(Location, PrimitiveType, u64), - /// Somehow we're trying to use a non-number as a number - NotANumber(Location, PrimitiveType), - /// The two types needed to be equivalent, but weren't. - NotEquivalent(Location, TypeOrVar, TypeOrVar), - /// We cannot safely cast the first type to the second type. - CannotSafelyCast(Location, PrimitiveType, PrimitiveType), - /// The primitive invocation provided the wrong number of arguments. - WrongPrimitiveArity(Location, Primitive, usize, usize, usize), - /// We cannot cast between the type types, for any number of reasons - CannotCast(Location, TypeOrVar, TypeOrVar), - /// We cannot turn a number into a function. - CannotMakeNumberAFunction(Location, TypeOrVar, Option), - /// We cannot turn a number into a Structure. - CannotMakeNumberAStructure(Location, TypeOrVar, Option), - /// We had a constraint we just couldn't solve. - CouldNotSolve(Constraint), - /// Functions are not printable. - FunctionsAreNotPrintable(Location), - /// The given type isn't signed, and can't be negated - IsNotSigned(Location, TypeOrVar), - /// The given type doesn't have the given field. - NoFieldForType(Location, ArcIntern, TypeOrVar), - /// There is no type with the given name. - UnknownTypeName(Location, ArcIntern), -} - -impl From for Diagnostic { - fn from(value: TypeInferenceError) -> Self { - match value { - TypeInferenceError::ConstantTooLarge(loc, primty, value) => loc - .labelled_error("constant too large for type") - .with_message(format!( - "Type {} has a max value of {}, which is smaller than {}", - primty, - primty.max_value().expect("constant type has max value"), - value - )), - TypeInferenceError::NotANumber(loc, primty) => loc - .labelled_error("not a numeric type") - .with_message(format!( - "For some reason, we're trying to use {} as a numeric type", - primty, - )), - TypeInferenceError::NotEquivalent(loc, ty1, ty2) => loc - .labelled_error("type inference error") - .with_message(format!("Expected type {}, received type {}", ty1, ty2)), - TypeInferenceError::CannotSafelyCast(loc, ty1, ty2) => loc - .labelled_error("unsafe type cast") - .with_message(format!("Cannot safely cast {} to {}", ty1, ty2)), - TypeInferenceError::WrongPrimitiveArity(loc, prim, lower, upper, observed) => loc - .labelled_error("wrong number of arguments") - .with_message(format!( - "expected {} for {}, received {}", - if lower == upper && lower > 1 { - format!("{} arguments", lower) - } else if lower == upper { - format!("{} argument", lower) - } else { - format!("{}-{} arguments", lower, upper) - }, - prim, - observed - )), - TypeInferenceError::CannotCast(loc, t1, t2) => loc - .labelled_error("cannot cast between types") - .with_message(format!( - "tried to cast from {} to {}", - t1, t2, - )), - TypeInferenceError::CannotMakeNumberAFunction(loc, t, val) => loc - .labelled_error(if let Some(val) = val { - format!("cannot turn {} into a function", val) - } else { - "cannot use a constant as a function type".to_string() - }) - .with_message(format!("function type was {}", t)), - TypeInferenceError::CannotMakeNumberAStructure(loc, t, val) => loc - .labelled_error(if let Some(val) = val { - format!("cannot turn {} into a function", val) - } else { - "cannot use a constant as a function type".to_string() - }) - .with_message(format!("function type was {}", t)), - TypeInferenceError::FunctionsAreNotPrintable(loc) => loc - .labelled_error("cannot print function values"), - TypeInferenceError::IsNotSigned(loc, pt) => loc - .labelled_error(format!("type {} is not signed", pt)) - .with_message("and so it cannot be negated"), - TypeInferenceError::NoFieldForType(loc, field, t) => loc - .labelled_error(format!("no field {} available for type {}", field, t)), - TypeInferenceError::UnknownTypeName(loc , name) => loc - .labelled_error(format!("unknown type named {}", name)), - TypeInferenceError::CouldNotSolve(Constraint::CanCastTo(loc, a, b)) => { - loc.labelled_error("internal error").with_message(format!( - "could not determine if it was safe to cast from {} to {}", - a, b - )) - } - TypeInferenceError::CouldNotSolve(Constraint::TypeHasField(loc, a, field, _)) => { - loc.labelled_error("internal error") - .with_message(format!("fould not determine if type {} has field {}", a, field)) - } - TypeInferenceError::CouldNotSolve(Constraint::Equivalent(loc, a, b)) => { - loc.labelled_error("internal error").with_message(format!( - "could not determine if {} and {} were equivalent", - a, b - )) - } - TypeInferenceError::CouldNotSolve(Constraint::FitsInNumType(loc, ty, val)) => { - loc.labelled_error("internal error").with_message(format!( - "Could not determine if {} could fit in {}", - val, ty - )) - } - TypeInferenceError::CouldNotSolve(Constraint::NumericType(loc, ty)) => loc - .labelled_error("internal error") - .with_message(format!("Could not determine if {} was a numeric type", ty)), - TypeInferenceError::CouldNotSolve(Constraint::ConstantNumericType(loc, ty)) => - panic!("What? Constants should always eventually be solved, even by default; {:?} and type {:?}", loc, ty), - TypeInferenceError::CouldNotSolve(Constraint::Printable(loc, ty)) => loc - .labelled_error("internal error") - .with_message(format!("Could not determine if type {} was printable", ty)), - TypeInferenceError::CouldNotSolve(Constraint::IsSomething(loc, _)) => { - loc.labelled_error("could not infer type") - .with_message("Could not find *any* type information; is this an unused function argument?") - } - TypeInferenceError::CouldNotSolve(Constraint::IsSigned(loc, t)) => loc - .labelled_error("internal error") - .with_message(format!("could not infer that type {} was signed", t)), - TypeInferenceError::CouldNotSolve(Constraint::NamedTypeIs(loc, name, ty)) => loc - .labelled_error("internal error") - .with_message(format!("could not infer that the name {} refers to {}", name, ty)), - } - } -} - -/// Warnings that we might want to tell the user about. -/// -/// These are fine, probably, but could indicate some behavior the user might not -/// expect, and so they might want to do something about them. -pub enum TypeInferenceWarning { - DefaultedTo(Location, TypeOrVar), -} - -impl From for Diagnostic { - fn from(value: TypeInferenceWarning) -> Self { - match value { - TypeInferenceWarning::DefaultedTo(loc, ty) => Diagnostic::warning() - .with_labels(vec![loc.primary_label().with_message("unknown type")]) - .with_message(format!("Defaulted unknown type to {}", ty)), - } - } -} - -/// Solve all the constraints in the provided database. -/// -/// This process can take a bit, so you might not want to do it multiple times. Basically, -/// it's going to grind on these constraints until either it figures them out, or it stops -/// making progress. I haven't done the math on the constraints to even figure out if this -/// is guaranteed to halt, though, let alone terminate in some reasonable amount of time. -/// -/// The return value is a type inference result, which pairs some warnings with either a -/// successful set of type resolutions (mappings from type variables to their values), or -/// a series of inference errors. -pub fn solve_constraints( - known_types: &HashMap, TypeOrVar>, - mut constraint_db: Vec, -) -> TypeInferenceResult { - let mut errors = vec![]; - let mut warnings = vec![]; - let mut iteration = 0u64; - - loop { - let mut changed_something = false; - let mut all_constraints_solved = true; - let mut new_constraints = vec![]; - - tracing::debug!(iteration, "Restarting constraint solving loop"); - for constraint in constraint_db.iter() { - tracing::debug!(%constraint, "remaining constraint"); - } - iteration += 1; - - while let Some(constraint) = constraint_db.pop() { - match constraint { - // The basic philosophy of this match block is that, for each constraint, we're - // going to start seeing if we can just solve (or abandon) the constraint. Then, - // if we can't, we'll just chuck it back on our list for later. - - // Checks on whether we can cast from one thing to another! - Constraint::CanCastTo( - loc, - TypeOrVar::Primitive(from_type), - TypeOrVar::Primitive(to_type), - ) => { - if !from_type.can_cast_to(&to_type) { - errors.push(TypeInferenceError::CannotSafelyCast( - loc, from_type, to_type, - )); - } - tracing::trace!(form = %from_type, to = %to_type, "we can determine if we can do the cast"); - changed_something = true; - } - - Constraint::CanCastTo( - loc, - TypeOrVar::Function(args1, ret1), - TypeOrVar::Function(args2, ret2), - ) => { - if args1.len() == args2.len() { - new_constraints.push(Constraint::Equivalent(loc.clone(), *ret1, *ret2)); - for (arg1, arg2) in args1.into_iter().zip(args2) { - new_constraints.push(Constraint::Equivalent(loc.clone(), arg1, arg2)) - } - all_constraints_solved = false; - } else { - errors.push(TypeInferenceError::CannotCast( - loc, - TypeOrVar::Function(args1, ret1), - TypeOrVar::Function(args2, ret2), - )); - } - tracing::trace!( - "we transferred CanCastTo to equivalence checks for function types" - ); - changed_something = true; - } - - Constraint::CanCastTo( - loc, - st1 @ TypeOrVar::Structure(_), - st2 @ TypeOrVar::Structure(_), - ) => { - tracing::trace!( - "structures can be equivalent, if their fields and types are exactly the same" - ); - new_constraints.push(Constraint::Equivalent(loc, st1, st2)); - changed_something = true; - } - - Constraint::CanCastTo( - loc, - ft @ TypeOrVar::Function(_, _), - ot @ TypeOrVar::Primitive(_) | ot @ TypeOrVar::Structure(_), - ) => { - tracing::trace!(function_type = %ft, other_type = %ot, "we can't cast a function type to a primitive or structure type"); - errors.push(TypeInferenceError::CannotCast(loc, ft, ot)); - changed_something = true; - } - - Constraint::CanCastTo( - loc, - pt @ TypeOrVar::Primitive(_), - ot @ TypeOrVar::Function(_, _) | ot @ TypeOrVar::Structure(_), - ) => { - tracing::trace!(other_type = %ot, primitive_type = %pt, "we can't cast a primitive type to a function or structure type"); - errors.push(TypeInferenceError::CannotCast(loc, pt, ot)); - changed_something = true; - } - - Constraint::CanCastTo( - loc, - st @ TypeOrVar::Structure(_), - ot @ TypeOrVar::Primitive(_) | ot @ TypeOrVar::Function(_, _), - ) => { - tracing::trace!(structure_type = %st, other_type = %ot, "we can't cast a structure type to a function or primitive type"); - errors.push(TypeInferenceError::CannotCast(loc, st, ot)); - changed_something = true; - } - - Constraint::NamedTypeIs(loc, name, ty) => match known_types.get(&name) { - None => { - tracing::trace!(type_name = %name, "we don't know a type named name"); - errors.push(TypeInferenceError::UnknownTypeName(loc, name)); - changed_something = true; - } - - Some(declared_type) => { - tracing::trace!(type_name = %name, declared = %declared_type, provided = %ty, "validating that named type is equivalent to provided"); - new_constraints.push(Constraint::Equivalent( - loc, - declared_type.clone(), - ty, - )); - changed_something = true; - } - }, - - Constraint::TypeHasField( - loc, - TypeOrVar::Structure(mut fields), - field, - result_type, - ) => match fields.remove_field(&field) { - None => { - let reconstituted = TypeOrVar::Structure(fields); - tracing::trace!(structure_type = %reconstituted, %field, "no field found in type"); - errors.push(TypeInferenceError::NoFieldForType( - loc, - field, - reconstituted, - )); - changed_something = true; - } - - Some(field_subtype) => { - tracing::trace!(%field_subtype, %result_type, %field, "validating that field's subtype matches target result type"); - new_constraints.push(Constraint::Equivalent( - loc, - result_type, - field_subtype, - )); - changed_something = true; - } - }, - - Constraint::TypeHasField( - loc, - ot @ TypeOrVar::Primitive(_) | ot @ TypeOrVar::Function(_, _), - field, - _, - ) => { - tracing::trace!(other_type = %ot, %field, "can't get field from primitive or function type"); - errors.push(TypeInferenceError::NoFieldForType(loc, field, ot)); - changed_something = true; - } - - // if we're testing if an actual primitive type is numeric, that's pretty easy - Constraint::ConstantNumericType(loc, TypeOrVar::Primitive(pt)) => { - tracing::trace!(primitive_type = %pt, "its easy to tell if a constant number can be a primitive type"); - if pt.max_value().is_none() { - errors.push(TypeInferenceError::NotANumber(loc, pt)) - } - changed_something = true; - } - - // if we're testing if a function type is numeric, then throw a useful warning - Constraint::ConstantNumericType(loc, t @ TypeOrVar::Function(_, _)) => { - tracing::trace!(function_type = %t, "functions can't be constant numbers"); - errors.push(TypeInferenceError::CannotMakeNumberAFunction(loc, t, None)); - changed_something = true; - } - - // if we're testing if a function type is numeric, then throw a useful warning - Constraint::ConstantNumericType(loc, t @ TypeOrVar::Structure(_)) => { - tracing::trace!(structure_type = %t, "structures can't be constant numbers"); - errors.push(TypeInferenceError::CannotMakeNumberAStructure(loc, t, None)); - changed_something = true; - } - - // if we're testing if a number can fit into a numeric type, we can just do that! - Constraint::FitsInNumType(loc, TypeOrVar::Primitive(ctype), val) => { - match ctype.max_value() { - None => errors.push(TypeInferenceError::NotANumber(loc, ctype)), - - Some(max_value) if max_value < val => { - errors.push(TypeInferenceError::ConstantTooLarge(loc, ctype, val)); - } - - Some(_) => {} - } - changed_something = true; - tracing::trace!(primitive_type = %ctype, value = val, "we can test for a value fitting in a primitive type"); - } - - // if we're testing if a function type can fit into a numeric type, that's a problem - Constraint::FitsInNumType(loc, t @ TypeOrVar::Function(_, _), val) => { - tracing::trace!(function_type = %t, "values don't fit in function types"); - errors.push(TypeInferenceError::CannotMakeNumberAFunction( + // Checks on whether we can cast from one thing to another! + Constraint::CanCastTo( loc, - t, - Some(val), - )); - changed_something = true; - } - - // if we're testing if a function type can fit into a numeric type, that's a problem - Constraint::FitsInNumType(loc, t @ TypeOrVar::Structure(_), val) => { - tracing::trace!(function_type = %t, "values don't fit in structure types"); - errors.push(TypeInferenceError::CannotMakeNumberAStructure( - loc, - t, - Some(val), - )); - changed_something = true; - } - - // if we want to know if a type is something, and it is something, then we're done - Constraint::IsSomething(_, t @ TypeOrVar::Function(_, _)) - | Constraint::IsSomething(_, t @ TypeOrVar::Primitive(_)) - | Constraint::IsSomething(_, t @ TypeOrVar::Structure(_)) => { - tracing::trace!(tested_type = %t, "type is definitely something"); - changed_something = true; - } - - // if we want to know if something is signed, we can check its primitive type - Constraint::IsSigned(loc, TypeOrVar::Primitive(pt)) => { - tracing::trace!(primitive_type = %pt, "we can check if a primitive is signed"); - if !pt.valid_operators().contains(&("-", 1)) { - errors.push(TypeInferenceError::IsNotSigned( - loc, - TypeOrVar::Primitive(pt), - )); - } - changed_something = true; - } - - // again with the functions and the numbers - Constraint::IsSigned(loc, t @ TypeOrVar::Function(_, _)) => { - tracing::trace!(function_type = %t, "functions are not signed"); - errors.push(TypeInferenceError::IsNotSigned(loc, t)); - changed_something = true; - } - - // again with the functions and the numbers - Constraint::IsSigned(loc, t @ TypeOrVar::Structure(_)) => { - tracing::trace!(structure_type = %t, "structures are not signed"); - errors.push(TypeInferenceError::IsNotSigned(loc, t)); - changed_something = true; - } - - // if we're testing if an actual primitive type is numeric, that's pretty easy - Constraint::NumericType(loc, TypeOrVar::Primitive(pt)) => { - tracing::trace!(primitive_type = %pt, "its easy to tell if a primitive type is numeric"); - if pt.max_value().is_none() { - errors.push(TypeInferenceError::NotANumber(loc, pt)) - } - changed_something = true; - } - - // if we're testing if a function type is numeric, then throw a useful warning - Constraint::NumericType(loc, t @ TypeOrVar::Function(_, _)) => { - tracing::trace!(function_type = %t, "function types aren't numeric"); - errors.push(TypeInferenceError::CannotMakeNumberAFunction(loc, t, None)); - changed_something = true; - } - - // if we're testing if a structure type is numeric, then throw a useful warning - Constraint::NumericType(loc, t @ TypeOrVar::Structure(_)) => { - tracing::trace!(structure_type = %t, "structure types aren't numeric"); - errors.push(TypeInferenceError::CannotMakeNumberAStructure(loc, t, None)); - changed_something = true; - } - - // all of our primitive types are printable - Constraint::Printable(_, TypeOrVar::Primitive(pt)) => { - tracing::trace!(primitive_type = %pt, "primitive types are printable"); - changed_something = true; - } - - // function types are definitely not printable - Constraint::Printable(loc, ft @ TypeOrVar::Function(_, _)) => { - tracing::trace!(function_type = %ft, "function types are not printable"); - errors.push(TypeInferenceError::FunctionsAreNotPrintable(loc)); - changed_something = true; - } - - // structure types are printable if all the types inside them are printable - Constraint::Printable(loc, TypeOrVar::Structure(fields)) => { - tracing::trace!( - "structure types are printable if all their subtypes are printable" - ); - for (_, subtype) in fields.into_iter() { - new_constraints.push(Constraint::Printable(loc.clone(), subtype)); - } - changed_something = true; - } - - // Some equivalences we can/should solve directly - Constraint::Equivalent( - loc, - TypeOrVar::Primitive(pt1), - TypeOrVar::Primitive(pt2), - ) => { - if pt1 != pt2 { - errors.push(TypeInferenceError::NotEquivalent( - loc, - TypeOrVar::Primitive(pt1), - TypeOrVar::Primitive(pt2), - )); - } - changed_something = true; - tracing::trace!(primitive_type1 = %pt1, primitive_type2 = %pt2, "we checked for primitive type equivalence"); - } - - Constraint::Equivalent( - loc, - pt @ TypeOrVar::Primitive(_), - ft @ TypeOrVar::Function(_, _), - ) - | Constraint::Equivalent( - loc, - ft @ TypeOrVar::Function(_, _), - pt @ TypeOrVar::Primitive(_), - ) => { - tracing::trace!(primitive_type = %pt, function_type = %ft, "function and primitive types cannot be equivalent"); - errors.push(TypeInferenceError::NotEquivalent(loc, pt, ft)); - changed_something = true; - } - - Constraint::Equivalent( - loc, - pt @ TypeOrVar::Primitive(_), - st @ TypeOrVar::Structure(_), - ) - | Constraint::Equivalent( - loc, - st @ TypeOrVar::Structure(_), - pt @ TypeOrVar::Primitive(_), - ) => { - tracing::trace!(primitive_type = %pt, structure_type = %st, "structure and primitive types cannot be equivalent"); - errors.push(TypeInferenceError::NotEquivalent(loc, pt, st)); - changed_something = true; - } - - Constraint::Equivalent( - loc, - st @ TypeOrVar::Structure(_), - ft @ TypeOrVar::Function(_, _), - ) - | Constraint::Equivalent( - loc, - ft @ TypeOrVar::Function(_, _), - st @ TypeOrVar::Structure(_), - ) => { - tracing::trace!(structure_type = %st, function_type = %ft, "structure and primitive types cannot be equivalent"); - errors.push(TypeInferenceError::NotEquivalent(loc, st, ft)); - changed_something = true; - } - - Constraint::Equivalent( - _, - TypeOrVar::Variable(_, name1), - TypeOrVar::Variable(_, name2), - ) if name1 == name2 => { - tracing::trace!(name = %name1, "variable is equivalent to itself"); - changed_something = true; - } - - Constraint::Equivalent( - loc, - TypeOrVar::Function(args1, ret1), - TypeOrVar::Function(args2, ret2), - ) => { - if args1.len() != args2.len() { - let t1 = TypeOrVar::Function(args1, ret1); - let t2 = TypeOrVar::Function(args2, ret2); - errors.push(TypeInferenceError::NotEquivalent(loc, t1, t2)); - } else { - for (left, right) in args1.into_iter().zip(args2) { - new_constraints.push(Constraint::Equivalent(loc.clone(), left, right)); - } - new_constraints.push(Constraint::Equivalent(loc, *ret1, *ret2)); - all_constraints_solved = false; - } - - changed_something = true; - tracing::trace!("we checked/rewrote if function types are equivalent"); - } - - Constraint::Equivalent( - loc, - TypeOrVar::Structure(fields1), - TypeOrVar::Structure(mut fields2), - ) => { - if fields1.count() == fields2.count() - && fields1.field_names().all(|x| fields2.has_field(x)) - { - for (name, subtype1) in fields1.into_iter() { - let subtype2 = fields2 - .remove_field(&name) - .expect("can find matching field after equivalence check"); - new_constraints.push(Constraint::Equivalent( - loc.clone(), - subtype1, - subtype2, + TypeOrVar::Primitive(from_type), + TypeOrVar::Primitive(to_type), + ) => { + if !from_type.can_cast_to(&to_type) { + self.errors.push(TypeInferenceError::CannotSafelyCast( + loc, from_type, to_type, )); } - } else { - errors.push(TypeInferenceError::NotEquivalent( - loc, - TypeOrVar::Structure(fields1), - TypeOrVar::Structure(fields2), - )) + tracing::trace!(form = %from_type, to = %to_type, "we can determine if we can do the cast"); + changed_something = true; } - changed_something = true; - tracing::trace!("we checked/rewrote if structures are equivalent"); - } - Constraint::Equivalent(_, TypeOrVar::Variable(_, ref name), ref rhs) => { - changed_something |= replace_variable(&mut constraint_db, name, rhs); - changed_something |= replace_variable(&mut new_constraints, name, rhs); - all_constraints_solved &= rhs.is_resolved(); - if changed_something { - tracing::trace!(%name, new_type = %rhs, "we were able to rewrite name somewhere"); + Constraint::CanCastTo( + loc, + TypeOrVar::Function(args1, ret1), + TypeOrVar::Function(args2, ret2), + ) => { + if args1.len() == args2.len() { + new_constraints.push(Constraint::Equivalent(loc.clone(), *ret1, *ret2)); + for (arg1, arg2) in args1.into_iter().zip(args2) { + new_constraints.push(Constraint::Equivalent( + loc.clone(), + arg1, + arg2, + )) + } + all_constraints_solved = false; + } else { + self.errors.push(TypeInferenceError::CannotCast( + loc, + TypeOrVar::Function(args1, ret1), + TypeOrVar::Function(args2, ret2), + )); + } + tracing::trace!( + "we transferred CanCastTo to equivalence checks for function types" + ); + changed_something = true; } - new_constraints.push(constraint); - } - Constraint::Equivalent(loc, lhs, rhs @ TypeOrVar::Variable(_, _)) => { - tracing::trace!(new_left = %rhs, new_right = %lhs, "we flipped the order on an equivalence"); - new_constraints.push(Constraint::Equivalent(loc, rhs, lhs)); - changed_something = true; - all_constraints_solved = false; - } + Constraint::CanCastTo( + loc, + st1 @ TypeOrVar::Structure(_), + st2 @ TypeOrVar::Structure(_), + ) => { + tracing::trace!( + "structures can be equivalent, if their fields and types are exactly the same" + ); + new_constraints.push(Constraint::Equivalent(loc, st1, st2)); + changed_something = true; + } - Constraint::CanCastTo(_, TypeOrVar::Variable(_, _), _) - | Constraint::CanCastTo(_, _, TypeOrVar::Variable(_, _)) - | Constraint::TypeHasField(_, TypeOrVar::Variable(_, _), _, _) - | Constraint::ConstantNumericType(_, TypeOrVar::Variable(_, _)) - | Constraint::FitsInNumType(_, TypeOrVar::Variable(_, _), _) - | Constraint::IsSomething(_, TypeOrVar::Variable(_, _)) - | Constraint::IsSigned(_, TypeOrVar::Variable(_, _)) - | Constraint::NumericType(_, TypeOrVar::Variable(_, _)) - | Constraint::Printable(_, TypeOrVar::Variable(_, _)) => { - all_constraints_solved = false; - new_constraints.push(constraint); - } - } - } + Constraint::CanCastTo( + loc, + ft @ TypeOrVar::Function(_, _), + ot @ TypeOrVar::Primitive(_) | ot @ TypeOrVar::Structure(_), + ) => { + tracing::trace!(function_type = %ft, other_type = %ot, "we can't cast a function type to a primitive or structure type"); + self.errors + .push(TypeInferenceError::CannotCast(loc, ft, ot)); + changed_something = true; + } - if all_constraints_solved { - let result = new_constraints - .into_iter() - .map(|constraint| match constraint { - Constraint::Equivalent(_, TypeOrVar::Variable(_, name), result) => { - match result.try_into() { - Err(e) => panic!("Ended up with complex type {}", e), - Ok(v) => (name, v), + Constraint::CanCastTo( + loc, + pt @ TypeOrVar::Primitive(_), + ot @ TypeOrVar::Function(_, _) | ot @ TypeOrVar::Structure(_), + ) => { + tracing::trace!(other_type = %ot, primitive_type = %pt, "we can't cast a primitive type to a function or structure type"); + self.errors + .push(TypeInferenceError::CannotCast(loc, pt, ot)); + changed_something = true; + } + + Constraint::CanCastTo( + loc, + st @ TypeOrVar::Structure(_), + ot @ TypeOrVar::Primitive(_) | ot @ TypeOrVar::Function(_, _), + ) => { + tracing::trace!(structure_type = %st, other_type = %ot, "we can't cast a structure type to a function or primitive type"); + self.errors + .push(TypeInferenceError::CannotCast(loc, st, ot)); + changed_something = true; + } + + Constraint::NamedTypeIs(loc, name, ty) => { + match self.type_definitions.get(&name) { + None => { + tracing::trace!(type_name = %name, "we don't know a type named name"); + self.errors + .push(TypeInferenceError::UnknownTypeName(loc, name)); + changed_something = true; + } + + Some(declared_type) => { + tracing::trace!(type_name = %name, declared = %declared_type, provided = %ty, "validating that named type is equivalent to provided"); + new_constraints.push(Constraint::Equivalent( + loc, + declared_type.clone(), + ty, + )); + changed_something = true; + } } } - _ => panic!("Had something that wasn't an equivalence left at the end!"), - }) - .collect(); - return TypeInferenceResult::Success { result, warnings }; - } - if !changed_something { - let mut addendums = vec![]; + Constraint::TypeHasField( + loc, + TypeOrVar::Structure(mut fields), + field, + result_type, + ) => match fields.remove_field(&field) { + None => { + let reconstituted = TypeOrVar::Structure(fields); + tracing::trace!(structure_type = %reconstituted, %field, "no field found in type"); + self.errors.push(TypeInferenceError::NoFieldForType( + loc, + field, + reconstituted, + )); + changed_something = true; + } - macro_rules! default_type { - ($addendums: ident, $loc: ident, $t: ident) => { - let resty = TypeOrVar::Primitive(PrimitiveType::U64); - $addendums.push(Constraint::Equivalent( - $loc.clone(), - $t.clone(), - resty.clone(), - )); - warnings.push(TypeInferenceWarning::DefaultedTo($loc.clone(), resty)); - tracing::trace!("Adding number equivalence"); - }; + Some(field_subtype) => { + tracing::trace!(%field_subtype, %result_type, %field, "validating that field's subtype matches target result type"); + new_constraints.push(Constraint::Equivalent( + loc, + result_type, + field_subtype, + )); + changed_something = true; + } + }, + + Constraint::TypeHasField( + loc, + ot @ TypeOrVar::Primitive(_) | ot @ TypeOrVar::Function(_, _), + field, + _, + ) => { + tracing::trace!(other_type = %ot, %field, "can't get field from primitive or function type"); + self.errors + .push(TypeInferenceError::NoFieldForType(loc, field, ot)); + changed_something = true; + } + + // if we're testing if an actual primitive type is numeric, that's pretty easy + Constraint::ConstantNumericType(loc, TypeOrVar::Primitive(pt)) => { + tracing::trace!(primitive_type = %pt, "its easy to tell if a constant number can be a primitive type"); + if pt.max_value().is_none() { + self.errors.push(TypeInferenceError::NotANumber(loc, pt)) + } + changed_something = true; + } + + // if we're testing if a function type is numeric, then throw a useful warning + Constraint::ConstantNumericType(loc, t @ TypeOrVar::Function(_, _)) => { + tracing::trace!(function_type = %t, "functions can't be constant numbers"); + self.errors + .push(TypeInferenceError::CannotMakeNumberAFunction(loc, t, None)); + changed_something = true; + } + + // if we're testing if a function type is numeric, then throw a useful warning + Constraint::ConstantNumericType(loc, t @ TypeOrVar::Structure(_)) => { + tracing::trace!(structure_type = %t, "structures can't be constant numbers"); + self.errors + .push(TypeInferenceError::CannotMakeNumberAStructure(loc, t, None)); + changed_something = true; + } + + // if we're testing if a number can fit into a numeric type, we can just do that! + Constraint::FitsInNumType(loc, TypeOrVar::Primitive(ctype), val) => { + match ctype.max_value() { + None => self.errors.push(TypeInferenceError::NotANumber(loc, ctype)), + + Some(max_value) if max_value < val => { + self.errors + .push(TypeInferenceError::ConstantTooLarge(loc, ctype, val)); + } + + Some(_) => {} + } + changed_something = true; + tracing::trace!(primitive_type = %ctype, value = val, "we can test for a value fitting in a primitive type"); + } + + // if we're testing if a function type can fit into a numeric type, that's a problem + Constraint::FitsInNumType(loc, t @ TypeOrVar::Function(_, _), val) => { + tracing::trace!(function_type = %t, "values don't fit in function types"); + self.errors + .push(TypeInferenceError::CannotMakeNumberAFunction( + loc, + t, + Some(val), + )); + changed_something = true; + } + + // if we're testing if a function type can fit into a numeric type, that's a problem + Constraint::FitsInNumType(loc, t @ TypeOrVar::Structure(_), val) => { + tracing::trace!(function_type = %t, "values don't fit in structure types"); + self.errors + .push(TypeInferenceError::CannotMakeNumberAStructure( + loc, + t, + Some(val), + )); + changed_something = true; + } + + // if we want to know if a type is something, and it is something, then we're done + Constraint::IsSomething(_, t @ TypeOrVar::Function(_, _)) + | Constraint::IsSomething(_, t @ TypeOrVar::Primitive(_)) + | Constraint::IsSomething(_, t @ TypeOrVar::Structure(_)) => { + tracing::trace!(tested_type = %t, "type is definitely something"); + changed_something = true; + } + + // if we want to know if something is signed, we can check its primitive type + Constraint::IsSigned(loc, TypeOrVar::Primitive(pt)) => { + tracing::trace!(primitive_type = %pt, "we can check if a primitive is signed"); + if !pt.valid_operators().contains(&("-", 1)) { + self.errors.push(TypeInferenceError::IsNotSigned( + loc, + TypeOrVar::Primitive(pt), + )); + } + changed_something = true; + } + + // again with the functions and the numbers + Constraint::IsSigned(loc, t @ TypeOrVar::Function(_, _)) => { + tracing::trace!(function_type = %t, "functions are not signed"); + self.errors.push(TypeInferenceError::IsNotSigned(loc, t)); + changed_something = true; + } + + // again with the functions and the numbers + Constraint::IsSigned(loc, t @ TypeOrVar::Structure(_)) => { + tracing::trace!(structure_type = %t, "structures are not signed"); + self.errors.push(TypeInferenceError::IsNotSigned(loc, t)); + changed_something = true; + } + + // if we're testing if an actual primitive type is numeric, that's pretty easy + Constraint::NumericType(loc, TypeOrVar::Primitive(pt)) => { + tracing::trace!(primitive_type = %pt, "its easy to tell if a primitive type is numeric"); + if pt.max_value().is_none() { + self.errors.push(TypeInferenceError::NotANumber(loc, pt)) + } + changed_something = true; + } + + // if we're testing if a function type is numeric, then throw a useful warning + Constraint::NumericType(loc, t @ TypeOrVar::Function(_, _)) => { + tracing::trace!(function_type = %t, "function types aren't numeric"); + self.errors + .push(TypeInferenceError::CannotMakeNumberAFunction(loc, t, None)); + changed_something = true; + } + + // if we're testing if a structure type is numeric, then throw a useful warning + Constraint::NumericType(loc, t @ TypeOrVar::Structure(_)) => { + tracing::trace!(structure_type = %t, "structure types aren't numeric"); + self.errors + .push(TypeInferenceError::CannotMakeNumberAStructure(loc, t, None)); + changed_something = true; + } + + // all of our primitive types are printable + Constraint::Printable(_, TypeOrVar::Primitive(pt)) => { + tracing::trace!(primitive_type = %pt, "primitive types are printable"); + changed_something = true; + } + + // function types are definitely not printable + Constraint::Printable(loc, ft @ TypeOrVar::Function(_, _)) => { + tracing::trace!(function_type = %ft, "function types are not printable"); + self.errors + .push(TypeInferenceError::FunctionsAreNotPrintable(loc)); + changed_something = true; + } + + // structure types are printable if all the types inside them are printable + Constraint::Printable(loc, TypeOrVar::Structure(fields)) => { + tracing::trace!( + "structure types are printable if all their subtypes are printable" + ); + for (_, subtype) in fields.into_iter() { + new_constraints.push(Constraint::Printable(loc.clone(), subtype)); + } + changed_something = true; + } + + // Some equivalences we can/should solve directly + Constraint::Equivalent( + loc, + TypeOrVar::Primitive(pt1), + TypeOrVar::Primitive(pt2), + ) => { + if pt1 != pt2 { + self.errors.push(TypeInferenceError::NotEquivalent( + loc, + TypeOrVar::Primitive(pt1), + TypeOrVar::Primitive(pt2), + )); + } + changed_something = true; + tracing::trace!(primitive_type1 = %pt1, primitive_type2 = %pt2, "we checked for primitive type equivalence"); + } + + Constraint::Equivalent( + loc, + pt @ TypeOrVar::Primitive(_), + ft @ TypeOrVar::Function(_, _), + ) + | Constraint::Equivalent( + loc, + ft @ TypeOrVar::Function(_, _), + pt @ TypeOrVar::Primitive(_), + ) => { + tracing::trace!(primitive_type = %pt, function_type = %ft, "function and primitive types cannot be equivalent"); + self.errors + .push(TypeInferenceError::NotEquivalent(loc, pt, ft)); + changed_something = true; + } + + Constraint::Equivalent( + loc, + pt @ TypeOrVar::Primitive(_), + st @ TypeOrVar::Structure(_), + ) + | Constraint::Equivalent( + loc, + st @ TypeOrVar::Structure(_), + pt @ TypeOrVar::Primitive(_), + ) => { + tracing::trace!(primitive_type = %pt, structure_type = %st, "structure and primitive types cannot be equivalent"); + self.errors + .push(TypeInferenceError::NotEquivalent(loc, pt, st)); + changed_something = true; + } + + Constraint::Equivalent( + loc, + st @ TypeOrVar::Structure(_), + ft @ TypeOrVar::Function(_, _), + ) + | Constraint::Equivalent( + loc, + ft @ TypeOrVar::Function(_, _), + st @ TypeOrVar::Structure(_), + ) => { + tracing::trace!(structure_type = %st, function_type = %ft, "structure and primitive types cannot be equivalent"); + self.errors + .push(TypeInferenceError::NotEquivalent(loc, st, ft)); + changed_something = true; + } + + Constraint::Equivalent( + _, + TypeOrVar::Variable(_, name1), + TypeOrVar::Variable(_, name2), + ) if name1 == name2 => { + tracing::trace!(name = %name1, "variable is equivalent to itself"); + changed_something = true; + } + + Constraint::Equivalent( + loc, + TypeOrVar::Function(args1, ret1), + TypeOrVar::Function(args2, ret2), + ) => { + if args1.len() != args2.len() { + let t1 = TypeOrVar::Function(args1, ret1); + let t2 = TypeOrVar::Function(args2, ret2); + self.errors + .push(TypeInferenceError::NotEquivalent(loc, t1, t2)); + } else { + for (left, right) in args1.into_iter().zip(args2) { + new_constraints.push(Constraint::Equivalent( + loc.clone(), + left, + right, + )); + } + new_constraints.push(Constraint::Equivalent(loc, *ret1, *ret2)); + all_constraints_solved = false; + } + + changed_something = true; + tracing::trace!("we checked/rewrote if function types are equivalent"); + } + + Constraint::Equivalent( + loc, + TypeOrVar::Structure(fields1), + TypeOrVar::Structure(mut fields2), + ) => { + if fields1.count() == fields2.count() + && fields1.field_names().all(|x| fields2.has_field(x)) + { + for (name, subtype1) in fields1.into_iter() { + let subtype2 = fields2 + .remove_field(&name) + .expect("can find matching field after equivalence check"); + new_constraints.push(Constraint::Equivalent( + loc.clone(), + subtype1, + subtype2, + )); + } + } else { + self.errors.push(TypeInferenceError::NotEquivalent( + loc, + TypeOrVar::Structure(fields1), + TypeOrVar::Structure(fields2), + )) + } + changed_something = true; + tracing::trace!("we checked/rewrote if structures are equivalent"); + } + + Constraint::Equivalent(_, TypeOrVar::Variable(_, ref name), ref rhs) => { + changed_something |= replace_variable(&mut self.constraints, name, rhs); + changed_something |= replace_variable(&mut new_constraints, name, rhs); + all_constraints_solved &= rhs.is_resolved(); + if changed_something { + tracing::trace!(%name, new_type = %rhs, "we were able to rewrite name somewhere"); + } + new_constraints.push(constraint); + } + + Constraint::Equivalent(loc, lhs, rhs @ TypeOrVar::Variable(_, _)) => { + tracing::trace!(new_left = %rhs, new_right = %lhs, "we flipped the order on an equivalence"); + new_constraints.push(Constraint::Equivalent(loc, rhs, lhs)); + changed_something = true; + all_constraints_solved = false; + } + + Constraint::CanCastTo(_, TypeOrVar::Variable(_, _), _) + | Constraint::CanCastTo(_, _, TypeOrVar::Variable(_, _)) + | Constraint::TypeHasField(_, TypeOrVar::Variable(_, _), _, _) + | Constraint::ConstantNumericType(_, TypeOrVar::Variable(_, _)) + | Constraint::FitsInNumType(_, TypeOrVar::Variable(_, _), _) + | Constraint::IsSomething(_, TypeOrVar::Variable(_, _)) + | Constraint::IsSigned(_, TypeOrVar::Variable(_, _)) + | Constraint::NumericType(_, TypeOrVar::Variable(_, _)) + | Constraint::Printable(_, TypeOrVar::Variable(_, _)) => { + all_constraints_solved = false; + new_constraints.push(constraint); + } + } } - new_constraints.retain(|x| { - if let Constraint::ConstantNumericType(loc, t) = x { - default_type!(addendums, loc, t); - false - } else { - true - } - }); + if all_constraints_solved { + return; + } + + if !changed_something { + let mut addendums = vec![]; + + macro_rules! default_type { + ($addendums: ident, $loc: ident, $t: ident) => { + let resty = TypeOrVar::Primitive(PrimitiveType::U64); + $addendums.push(Constraint::Equivalent( + $loc.clone(), + $t.clone(), + resty.clone(), + )); + self.warnings + .push(TypeInferenceWarning::DefaultedTo($loc.clone(), resty)); + tracing::trace!("Adding number equivalence"); + }; + } - if addendums.is_empty() { new_constraints.retain(|x| { - if let Constraint::IsSomething(loc, t) = x { + if let Constraint::ConstantNumericType(loc, t) = x { default_type!(addendums, loc, t); false } else { true } }); - } - if addendums.is_empty() { - if errors.is_empty() { - errors = new_constraints - .into_iter() - .map(TypeInferenceError::CouldNotSolve) - .collect(); + if addendums.is_empty() { + new_constraints.retain(|x| { + if let Constraint::IsSomething(loc, t) = x { + default_type!(addendums, loc, t); + false + } else { + true + } + }); } - return TypeInferenceResult::Failure { errors, warnings }; + + if addendums.is_empty() { + if self.errors.is_empty() { + self.errors = new_constraints + .into_iter() + .map(TypeInferenceError::CouldNotSolve) + .collect(); + } + return; + } + + new_constraints.append(&mut addendums); } - new_constraints.append(&mut addendums); + self.constraints = new_constraints; } - - constraint_db = new_constraints; } } diff --git a/src/type_infer/warning.rs b/src/type_infer/warning.rs new file mode 100644 index 0000000..c5db275 --- /dev/null +++ b/src/type_infer/warning.rs @@ -0,0 +1,21 @@ +use crate::ir::TypeOrVar; +use crate::syntax::Location; +use codespan_reporting::diagnostic::Diagnostic; + +/// Warnings that we might want to tell the user about. +/// +/// These are fine, probably, but could indicate some behavior the user might not +/// expect, and so they might want to do something about them. +pub enum TypeInferenceWarning { + DefaultedTo(Location, TypeOrVar), +} + +impl From for Diagnostic { + fn from(value: TypeInferenceWarning) -> Self { + match value { + TypeInferenceWarning::DefaultedTo(loc, ty) => Diagnostic::warning() + .with_labels(vec![loc.primary_label().with_message("unknown type")]) + .with_message(format!("Defaulted unknown type to {}", ty)), + } + } +} -- 2.53.0 From 4c2850427a896e7320bd7e6f11fc8a0dc7d950fd Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Mon, 29 Apr 2024 21:38:17 -0700 Subject: [PATCH 53/59] Start isolating names into their own thing. --- src/lambda_lift.rs | 8 +++ src/lib.rs | 1 + src/repl.rs | 6 +- src/syntax.rs | 2 + src/syntax/arbitrary.rs | 15 ++--- src/syntax/ast.rs | 66 ++-------------------- src/syntax/eval.rs | 18 +++--- src/syntax/name.rs | 113 ++++++++++++++++++++++++++++++++++++++ src/syntax/parser.lalrpop | 5 +- src/syntax/pretty.rs | 4 +- src/syntax/validate.rs | 12 ++-- src/type_infer/convert.rs | 37 +++++++------ 12 files changed, 176 insertions(+), 111 deletions(-) create mode 100644 src/lambda_lift.rs create mode 100644 src/syntax/name.rs diff --git a/src/lambda_lift.rs b/src/lambda_lift.rs new file mode 100644 index 0000000..8880273 --- /dev/null +++ b/src/lambda_lift.rs @@ -0,0 +1,8 @@ +use crate::syntax::{Expression, Name}; +use std::collections::{HashSet, HashMap}; + +impl Expression { + fn free_variables(&self) -> HashSet { + unimplemented!() + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 23f924c..fcb21f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,6 +66,7 @@ pub mod eval; #[cfg(test)] mod examples; pub mod ir; +pub mod lambda_lift; pub mod syntax; pub mod type_infer; pub mod util; diff --git a/src/repl.rs b/src/repl.rs index d2730ef..473dfa4 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -134,8 +134,8 @@ impl REPL { // if this is a variable binding, and we've never defined this variable before, // we should tell cranelift about it. this is optimistic; if we fail to compile, // then we won't use this definition until someone tries again. - if !self.variable_binding_sites.contains_key(&name.name) { - self.jitter.define_string(&name.name)?; + if !self.variable_binding_sites.contains_key(&name.current_name().to_string()) { + self.jitter.define_string(name.current_name())?; self.jitter .define_variable(name.to_string(), ConstantType::U64)?; } @@ -149,7 +149,7 @@ impl REPL { loc.clone(), crate::syntax::Name::manufactured("print"), )), - vec![Expression::Reference(loc.clone(), name.name)], + vec![Expression::Reference(name.clone())], )), ], } diff --git a/src/syntax.rs b/src/syntax.rs index 309f7ec..1e279d5 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -31,6 +31,7 @@ pub mod arbitrary; mod ast; pub mod eval; mod location; +mod name; mod tokens; lalrpop_mod!( #[allow(clippy::just_underscores_and_digits, clippy::clone_on_copy)] @@ -44,6 +45,7 @@ mod validate; use crate::syntax::arbitrary::GenerationEnvironment; pub use crate::syntax::ast::*; pub use crate::syntax::location::Location; +pub use crate::syntax::name::Name; pub use crate::syntax::parser::{ProgramParser, TopLevelParser}; pub use crate::syntax::tokens::{LexerError, Token}; use lalrpop_util::ParseError; diff --git a/src/syntax/arbitrary.rs b/src/syntax/arbitrary.rs index e9ab301..c87d941 100644 --- a/src/syntax/arbitrary.rs +++ b/src/syntax/arbitrary.rs @@ -1,4 +1,5 @@ -use crate::syntax::ast::{ConstantType, Expression, Name, Program, TopLevel, Value}; +use crate::syntax::ast::{ConstantType, Expression, Program, TopLevel, Value}; +use crate::syntax::name::Name; use crate::syntax::location::Location; use proptest::sample::select; use proptest::{ @@ -88,10 +89,7 @@ impl Arbitrary for Program { Location::manufactured(), Name::manufactured("print"), )), - vec![Expression::Reference( - Location::manufactured(), - n.to_string(), - )], + vec![Expression::Reference(n.clone())], ))) }); items.push(Union::new(printers).boxed()); @@ -168,12 +166,7 @@ impl Arbitrary for Expression { } else { let mut strats = bound_variables_of_type .drain(..) - .map(|x| { - Just(Expression::Reference( - Location::manufactured(), - x.name.clone(), - )) - .boxed() + .map(|x| { Just(Expression::Reference(x.clone())).boxed() }) .collect::>(); strats.push(value_strategy); diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index e41d443..c0fa09f 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -1,10 +1,6 @@ -use std::fmt; -use std::hash::Hash; - -use internment::ArcIntern; - -pub use crate::syntax::tokens::ConstantType; +use crate::syntax::name::Name; use crate::syntax::Location; +pub use crate::syntax::tokens::ConstantType; /// A structure represented a parsed program. /// @@ -30,56 +26,6 @@ pub enum TopLevel { Structure(Location, Name, Vec<(Name, Type)>), } -/// 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) - } -} - /// An expression in the underlying syntax. /// /// Like statements, these expressions are guaranteed to have been @@ -90,7 +36,7 @@ impl fmt::Display for Name { pub enum Expression { Value(Location, Value), Constructor(Location, Name, Vec<(Name, Expression)>), - Reference(Location, String), + Reference(Name), FieldRef(Location, Box, Name), Cast(Location, String, Box), Primitive(Location, Name), @@ -130,8 +76,8 @@ impl PartialEq for Expression { Expression::Constructor(_, name2, fields2) => name1 == name2 && fields1 == fields2, _ => false, }, - Expression::Reference(_, var1) => match other { - Expression::Reference(_, var2) => var1 == var2, + Expression::Reference(var1) => match other { + Expression::Reference(var2) => var1 == var2, _ => false, }, Expression::FieldRef(_, exp1, field1) => match other { @@ -174,7 +120,7 @@ impl Expression { match self { Expression::Value(loc, _) => loc, Expression::Constructor(loc, _, _) => loc, - Expression::Reference(loc, _) => loc, + Expression::Reference(n) => n.location(), Expression::FieldRef(loc, _, _) => loc, Expression::Cast(loc, _, _) => loc, Expression::Primitive(loc, _) => loc, diff --git a/src/syntax/eval.rs b/src/syntax/eval.rs index ae45ed8..58ac657 100644 --- a/src/syntax/eval.rs +++ b/src/syntax/eval.rs @@ -1,5 +1,5 @@ use crate::eval::{EvalError, PrimitiveType, Value}; -use crate::syntax::{ConstantType, Expression, Name, Program, TopLevel}; +use crate::syntax::{ConstantType, Expression, Program, TopLevel}; use crate::util::scoped_map::ScopedMap; use internment::ArcIntern; use std::collections::HashMap; @@ -69,9 +69,9 @@ impl Expression { Ok(Value::Structure(Some(on.clone().intern()), map)) } - Expression::Reference(loc, n) => env - .get(&ArcIntern::new(n.clone())) - .ok_or_else(|| EvalError::LookupFailed(loc.clone(), n.clone())) + Expression::Reference(n) => env + .get(n.current_interned()) + .ok_or_else(|| EvalError::LookupFailed(n.location().clone(), n.current_name().to_string())) .cloned(), Expression::FieldRef(loc, expr, field) => { @@ -102,7 +102,7 @@ impl Expression { Ok(target_type.safe_cast(&value)?) } - Expression::Primitive(_, op) => Ok(Value::primitive(op.name.clone())), + Expression::Primitive(_, op) => Ok(Value::primitive(op.original_name().to_string())), Expression::Call(loc, fun, args) => { let function = fun.eval(stdout, env)?; @@ -129,8 +129,8 @@ impl Expression { } Value::Primitive(name) if name == "print" => { - if let [Expression::Reference(_, name)] = &args[..] { - let value = Expression::Reference(loc.clone(), name.clone()) + if let [Expression::Reference(name)] = &args[..] { + let value = Expression::Reference(name.clone()) .eval(stdout, env)?; let value = match value { Value::Number(x) => Value::U64(x), @@ -178,12 +178,12 @@ impl Expression { Expression::Function(_, name, arg_names, _, body) => { let result = Value::Closure( - name.clone().map(Name::intern), + name.as_ref().map(|n| n.current_interned().clone()), env.clone(), arg_names .iter() .cloned() - .map(|(x, _)| Name::intern(x)) + .map(|(x, _)| x.current_interned().clone()) .collect(), *body.clone(), ); diff --git a/src/syntax/name.rs b/src/syntax/name.rs new file mode 100644 index 0000000..5ffc5b5 --- /dev/null +++ b/src/syntax/name.rs @@ -0,0 +1,113 @@ +use crate::syntax::Location; +use internment::ArcIntern; +use std::fmt; +use std::hash::Hash; + +/// The name of a thing in the source language. +/// +/// In many ways, you can treat this like a string, but it's a very tricky +/// string in a couple of ways: +/// +/// First, it's a string associated with a particular location in the source +/// file, and you can find out what that source location is relatively easily. +/// +/// Second, it's a name that retains something of its identity across renaming, +/// so that you can keep track of what a variables original name was, as well as +/// what it's new name is if it's been renamed. +/// +/// Finally, when it comes to equality tests, comparisons, and hashing, `Name` +/// uses *only* the new name, if the variable has been renamed, or the original +/// name, if it has not been renamed. It never uses the location. This allows +/// relatively fast hashing and searching for things like binding sites, as the +/// value of the binding `Name` will be equal to the bound `Name`, even though +/// they occur at different locations. +#[derive(Clone, Debug)] +pub struct Name { + name: ArcIntern, + rename: Option>, + location: Location, +} + +impl Name { + /// Create a new name at the given location. + /// + /// This creates an "original" name, which has not been renamed, at the + /// given location. + pub fn new(n: S, location: Location) -> Name { + Name { + name: ArcIntern::new(n.to_string()), + rename: None, + location, + } + } + + /// Create a new name with no location information. + /// + /// This creates an "original" name, which has not been renamed, at the + /// given location. You should always prefer to use [`Location::new`] if + /// there is any possible way to get it, because that will be more + /// helpful to our users. + pub fn manufactured(n: S) -> Name { + Name { + name: ArcIntern::new(n.to_string()), + rename: None, + location: Location::manufactured(), + } + } + + /// Returns a reference to the original name of the variable. + /// + /// Regardless of whether or not the function has been renamed, this will + /// return whatever name this variable started with. + pub fn original_name(&self) -> &str { + self.name.as_str() + } + + /// Returns a reference to the current name of the variable. + /// + /// If the variable has been renamed, it will return that, otherwise we'll + /// return the current name. + pub fn current_name(&self) -> &str { + self.rename.as_ref().map(|x| x.as_str()).unwrap_or_else(|| self.name.as_str()) + } + + /// Returns the current name of the variable as an interned string. + pub fn current_interned(&self) -> &ArcIntern { + self.rename.as_ref().unwrap_or(&self.name) + } + + /// Return the location of this name. + pub fn location(&self) -> &Location { + &self.location + } + + /// Rename this variable to the given value + pub fn rename(&mut self, new_name: &ArcIntern) { + self.rename = Some(new_name.clone()); + } + + pub fn intern(&self) -> ArcIntern { + self.current_interned().clone() + } +} + +impl PartialEq for Name { + fn eq(&self, other: &Self) -> bool { + self.current_interned() == other.current_interned() + } +} + +impl Eq for Name {} + +impl Hash for Name { + fn hash(&self, state: &mut H) { + self.current_interned().hash(state) + } +} + +impl fmt::Display for Name { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.current_name().fmt(f) + } +} + diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index 4522807..6c4d53d 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -9,7 +9,8 @@ //! eventually want to leave lalrpop behind.) //! use crate::syntax::{Location, ParserError}; -use crate::syntax::ast::{Program,TopLevel,Expression,Value,Name,Type}; +use crate::syntax::ast::{Program,TopLevel,Expression,Value,Type}; +use crate::syntax::name::Name; use crate::syntax::tokens::{ConstantType, Token}; use internment::ArcIntern; @@ -241,7 +242,7 @@ FieldExpression: Expression = { // they cannot be further divided into parts AtomicExpression: Expression = { // just a variable reference - "> => Expression::Reference(Location::new(file_idx, l..end), v.to_string()), + "> => Expression::Reference(Name::new(v.to_string(), Location::new(file_idx, l..end))), // just a number "> => Expression::Value(Location::new(file_idx, l..end), Value::Number(n.0, n.1, n.2)), // this expression could actually be a block! diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index a9caf3b..3ba412e 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -67,7 +67,7 @@ impl Expression { .nest(2) .braces(), ), - Expression::Reference(_, var) => allocator.text(var.to_string()), + Expression::Reference(var) => allocator.text(var.to_string()), Expression::FieldRef(_, val, field) => val .pretty(allocator) .append(allocator.text(".")) @@ -76,7 +76,7 @@ impl Expression { .text(t.clone()) .angles() .append(e.pretty(allocator)), - Expression::Primitive(_, op) => allocator.text(op.name.clone()), + Expression::Primitive(_, op) => allocator.text(op.original_name().to_string()), Expression::Call(_, fun, args) => { let args = args.iter().map(|x| x.pretty(allocator)); let comma_sepped_args = allocator.intersperse(args, allocator.text(",")); diff --git a/src/syntax/validate.rs b/src/syntax/validate.rs index afbba2c..4db79c8 100644 --- a/src/syntax/validate.rs +++ b/src/syntax/validate.rs @@ -141,9 +141,9 @@ impl Expression { (errors, warnings) } - Expression::Reference(_, var) if variable_map.contains_key(var) => (vec![], vec![]), - Expression::Reference(loc, var) => ( - vec![Error::UnboundVariable(loc.clone(), var.clone())], + Expression::Reference(var) if variable_map.contains_key(&var.original_name().to_string()) => (vec![], vec![]), + Expression::Reference(var) => ( + vec![Error::UnboundVariable(var.location().clone(), var.original_name().to_string())], vec![], ), Expression::FieldRef(_, exp, _) => exp.validate(variable_map), @@ -187,7 +187,7 @@ impl Expression { // immediately check the expression, and go from there. let (errors, mut warnings) = val.validate(variable_map); - if let Some(original_binding_site) = variable_map.get(&var.name) { + if let Some(original_binding_site) = variable_map.get(&var.original_name().to_string()) { warnings.push(Warning::ShadowedVariable( original_binding_site.clone(), loc.clone(), @@ -201,11 +201,11 @@ impl Expression { } Expression::Function(_, name, arguments, _, body) => { if let Some(name) = name { - variable_map.insert(name.name.clone(), name.location.clone()); + variable_map.insert(name.original_name().to_string(), name.location().clone()); } variable_map.new_scope(); for (arg, _) in arguments.iter() { - variable_map.insert(arg.name.clone(), arg.location.clone()); + variable_map.insert(arg.original_name().to_string(), arg.location().clone()); } let result = body.validate(variable_map); variable_map.release_scope(); diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 8266eee..225f9db 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -191,20 +191,21 @@ impl InferenceEngine { } } - syntax::Expression::Reference(loc, name) => { - let iname = ArcIntern::new(name); - let final_name = renames.get(&iname).cloned().unwrap_or(iname); + syntax::Expression::Reference(mut name) => { + if let Some(rename) = renames.get(name.current_interned()) { + name.rename(rename); + } let result_type = self .variable_types - .get(&final_name) + .get(name.current_interned()) .cloned() .expect("variable bound before use"); let expression = ir::Expression::Atomic(ir::ValueOrRef::Ref( - loc, + name.location().clone(), result_type.clone(), - final_name.clone(), + name.current_interned().clone(), )); - let free_variables = HashSet::from([final_name]); + let free_variables = HashSet::from([name.current_interned().clone()]); ExpressionInfo { expression, @@ -260,7 +261,7 @@ impl InferenceEngine { } syntax::Expression::Primitive(loc, name) => { - let primop = ir::Primitive::from_str(&name.name).expect("valid primitive"); + let primop = ir::Primitive::from_str(&name.current_name()).expect("valid primitive"); match primop { ir::Primitive::Plus | ir::Primitive::Times | ir::Primitive::Divide => { @@ -404,12 +405,12 @@ impl InferenceEngine { expr_info } - syntax::Expression::Function(_, name, args, _, expr) => { + syntax::Expression::Function(loc, name, args, _, expr) => { // First, at some point we're going to want to know a location for this function, // which should either be the name if we have one, or the body if we don't. let function_location = match name { None => expr.location().clone(), - Some(ref name) => name.location.clone(), + Some(ref name) => loc, }; // Next, let us figure out what we're going to name this function. If the user // didn't provide one, we'll just call it "function:" for them. (We'll @@ -440,7 +441,7 @@ impl InferenceEngine { .map(|(name, mut declared_type)| { let new_type = ir::TypeOrVar::new(); self.constraints.push(Constraint::IsSomething( - name.location.clone(), + name.location().clone(), new_type.clone(), )); let new_name = self.finalize_name(renames, name.clone()); @@ -450,7 +451,7 @@ impl InferenceEngine { if let Some(declared_type) = declared_type.take() { let declared_type = self.convert_type(declared_type); self.constraints.push(Constraint::Equivalent( - name.location.clone(), + name.location().clone(), new_type.clone(), declared_type, )); @@ -495,11 +496,11 @@ impl InferenceEngine { fn convert_type(&mut self, ty: syntax::Type) -> ir::TypeOrVar { match ty { - syntax::Type::Named(x) => match PrimitiveType::from_str(x.name.as_str()) { + syntax::Type::Named(x) => match PrimitiveType::from_str(x.current_name()) { Err(_) => { - let retval = ir::TypeOrVar::new_located(x.location.clone()); + let retval = ir::TypeOrVar::new_located(x.location().clone()); self.constraints.push(Constraint::NamedTypeIs( - x.location.clone(), + x.location().clone(), x.intern(), retval.clone(), )); @@ -529,10 +530,10 @@ impl InferenceEngine { ) -> ArcIntern { if self .variable_types - .contains_key(&ArcIntern::new(name.name.clone())) + .contains_key(name.current_interned()) { - let new_name = ir::gensym(&name.name); - renames.insert(ArcIntern::new(name.name.to_string()), new_name.clone()); + let new_name = ir::gensym(&name.original_name()); + renames.insert(name.current_interned().clone(), new_name.clone()); new_name } else { ArcIntern::new(name.to_string()) -- 2.53.0 From afff04259c09db4ba5bad5a92e68fc0f0aea9714 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Mon, 13 May 2024 20:47:11 -0700 Subject: [PATCH 54/59] Free variable analysis. --- src/lambda_lift.rs | 9 +- src/lambda_lift/free_variables.rs | 161 ++++++++++++++++++++++++++++++ src/syntax.rs | 19 +++- src/syntax/parser.lalrpop | 4 +- src/type_infer/convert.rs | 8 +- 5 files changed, 186 insertions(+), 15 deletions(-) create mode 100644 src/lambda_lift/free_variables.rs diff --git a/src/lambda_lift.rs b/src/lambda_lift.rs index 8880273..339b1c0 100644 --- a/src/lambda_lift.rs +++ b/src/lambda_lift.rs @@ -1,8 +1 @@ -use crate::syntax::{Expression, Name}; -use std::collections::{HashSet, HashMap}; - -impl Expression { - fn free_variables(&self) -> HashSet { - unimplemented!() - } -} \ No newline at end of file +mod free_variables; diff --git a/src/lambda_lift/free_variables.rs b/src/lambda_lift/free_variables.rs new file mode 100644 index 0000000..2f8f868 --- /dev/null +++ b/src/lambda_lift/free_variables.rs @@ -0,0 +1,161 @@ +use crate::syntax::{Expression, Name}; +use std::collections::HashSet; + +impl Expression { + /// Find the set of free variables used within this expression. + /// + /// Obviously, if this expression contains a function definition, argument + /// variables in the body will not be reported as free. + pub fn free_variables(&self) -> HashSet { + match self { + Expression::Value(_, _) => HashSet::new(), + Expression::Constructor(_, _, args) => + args.iter().fold(HashSet::new(), |mut existing, (_, expr)| { + existing.extend(expr.free_variables()); + existing + }), + Expression::Reference(n) => HashSet::from([n.clone()]), + Expression::FieldRef(_, expr, _) => expr.free_variables(), + Expression::Cast(_, _, expr) => expr.free_variables(), + Expression::Primitive(_, _) => HashSet::new(), + Expression::Call(_, f, args) => + args.iter() + .fold(f.free_variables(), + |mut existing, expr| { + existing.extend(expr.free_variables()); + existing + }), + Expression::Block(_, exprs) => { + let mut free_vars = HashSet::new(); + let mut bound_vars = HashSet::new(); + + for expr in exprs.iter() { + for var in expr.free_variables().into_iter() { + if !bound_vars.contains(&var) { + free_vars.insert(var); + } + } + + bound_vars.extend(expr.new_bindings()); + } + + free_vars + } + Expression::Binding(_, _, expr) => expr.free_variables(), + Expression::Function(_, name, args, _, body) => { + let mut candidates = body.free_variables(); + if let Some(name) = name { + candidates.remove(name); + } + for (name, _) in args.iter() { + candidates.remove(name); + } + candidates + } + } + } + + /// Find the set of new bindings in the provided expression. + /// + /// New bindings are those that introduce a variable that can be + /// referenced in subsequent statements / expressions within a + /// parent construct. This eventually means something in the next + /// block, but can involve some odd effects in the language. + pub fn new_bindings(&self) -> HashSet { + match self { + Expression::Value(_, _) => HashSet::new(), + Expression::Constructor(_, _, args) => + args.iter().fold(HashSet::new(), |mut existing, (_, expr)| { + existing.extend(expr.new_bindings()); + existing + }), + Expression::Reference(_) => HashSet::new(), + Expression::FieldRef(_, expr, _) => expr.new_bindings(), + Expression::Cast(_, _, expr) => expr.new_bindings(), + Expression::Primitive(_, _) => HashSet::new(), + Expression::Call(_, f, args) => + args.iter() + .fold(f.new_bindings(), + |mut existing, expr| { + existing.extend(expr.new_bindings()); + existing + }), + Expression::Block(_, _) => HashSet::new(), + Expression::Binding(_, name, expr) => { + let mut others = expr.new_bindings(); + others.insert(name.clone()); + others + } + Expression::Function(_, Some(name), _, _, _) => HashSet::from([name.clone()]), + Expression::Function(_, None, _, _, _) => HashSet::new(), + } + } +} + +#[test] +fn basic_frees_works() { + let test = Expression::parse(0, "1u64").unwrap(); + assert_eq!(0, test.free_variables().len()); + + let test = Expression::parse(0, "1u64 + 2").unwrap(); + assert_eq!(0, test.free_variables().len()); + + let test = Expression::parse(0, "x").unwrap(); + assert_eq!(1, test.free_variables().len()); + assert!(test.free_variables().contains(&Name::manufactured("x"))); + + let test = Expression::parse(0, "1 + x").unwrap(); + assert_eq!(1, test.free_variables().len()); + assert!(test.free_variables().contains(&Name::manufactured("x"))); + + let test = Expression::parse(0, "Structure{ field1: x; field2: y; }").unwrap(); + assert_eq!(2, test.free_variables().len()); + assert!(test.free_variables().contains(&Name::manufactured("x"))); + assert!(test.free_variables().contains(&Name::manufactured("y"))); + + let test = Expression::parse(0, "{ print x; print y }").unwrap(); + assert_eq!(2, test.free_variables().len()); + assert!(test.free_variables().contains(&Name::manufactured("x"))); + assert!(test.free_variables().contains(&Name::manufactured("y"))); +} + +#[test] +fn test_around_function() { + let nada = Expression::parse(0, "function(x) x").unwrap(); + assert_eq!(0, nada.free_variables().len()); + + let lift = Expression::parse(0, "function(x) x + y").unwrap(); + assert_eq!(1, lift.free_variables().len()); + assert!(lift.free_variables().contains(&Name::manufactured("y"))); + + let nest = Expression::parse(0, "function(y) function(x) x + y").unwrap(); + assert_eq!(0, nest.free_variables().len()); + + let multi = Expression::parse(0, "function(x, y) x + y + z").unwrap(); + assert_eq!(1, multi.free_variables().len()); + assert!(multi.free_variables().contains(&Name::manufactured("z"))); +} + +#[test] +fn test_is_set() { + let multi = Expression::parse(0, "function(x, y) x + y + z + z").unwrap(); + assert_eq!(1, multi.free_variables().len()); + assert!(multi.free_variables().contains(&Name::manufactured("z"))); +} + +#[test] +fn bindings_remove() { + let x_bind = Expression::parse(0, "{ x = 4; print x }").unwrap(); + assert_eq!(0, x_bind.free_variables().len()); + + let inner = Expression::parse(0, "{ { x = 4; print x }; print y }").unwrap(); + assert_eq!(1, inner.free_variables().len()); + assert!(inner.free_variables().contains(&Name::manufactured("y"))); + + let inner = Expression::parse(0, "{ { x = 4; print x }; print x }").unwrap(); + assert_eq!(1, inner.free_variables().len()); + assert!(inner.free_variables().contains(&Name::manufactured("x"))); + + let double = Expression::parse(0, "{ x = y = 1; x + y }").unwrap(); + assert_eq!(0, double.free_variables().len()); +} diff --git a/src/syntax.rs b/src/syntax.rs index 1e279d5..7e54c31 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -46,7 +46,7 @@ use crate::syntax::arbitrary::GenerationEnvironment; pub use crate::syntax::ast::*; pub use crate::syntax::location::Location; pub use crate::syntax::name::Name; -pub use crate::syntax::parser::{ProgramParser, TopLevelParser}; +pub use crate::syntax::parser::{ProgramParser, TopLevelParser, ExpressionParser}; pub use crate::syntax::tokens::{LexerError, Token}; use lalrpop_util::ParseError; #[cfg(test)] @@ -257,6 +257,23 @@ impl TopLevel { } } +impl Expression { + /// Parse an expression from a string, using the given index for [`Location`]s. + /// + /// As with [`Program::parse`], if you use a bad file index, you'll get weird behaviors + /// when you try to print errors, but things should otherwise work fine. This function + /// will only parse a single expression, which is useful for testing, but probably shouldn't + /// be used when reading in whole files. + pub fn parse(file_idx: usize, buffer: &str) -> Result { + let lexer = Token::lexer(buffer) + .spanned() + .map(|x| permute_lexer_result(file_idx, x)); + ExpressionParser::new() + .parse(file_idx, lexer) + .map_err(|e| ParserError::convert(file_idx, e)) + } +} + fn permute_lexer_result( file_idx: usize, result: (Result, Range), diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index 6c4d53d..e2777b5 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -150,7 +150,7 @@ TypeName: Name = { // to run through a few examples. Consider thinking about how you want to // parse something like "1 + 2 * 3", for example, versus "1 + 2 + 3" or // "1 * 2 + 3", and hopefully that'll help. -Expression: Expression = { +pub Expression: Expression = { BindingExpression, } @@ -271,4 +271,4 @@ Comma: Vec = { v } } -}; \ No newline at end of file +}; diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 225f9db..2eac333 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -261,7 +261,7 @@ impl InferenceEngine { } syntax::Expression::Primitive(loc, name) => { - let primop = ir::Primitive::from_str(&name.current_name()).expect("valid primitive"); + let primop = ir::Primitive::from_str(name.current_name()).expect("valid primitive"); match primop { ir::Primitive::Plus | ir::Primitive::Times | ir::Primitive::Divide => { @@ -409,8 +409,8 @@ impl InferenceEngine { // First, at some point we're going to want to know a location for this function, // which should either be the name if we have one, or the body if we don't. let function_location = match name { - None => expr.location().clone(), - Some(ref name) => loc, + None => loc, + Some(ref name) => name.location().clone(), }; // Next, let us figure out what we're going to name this function. If the user // didn't provide one, we'll just call it "function:" for them. (We'll @@ -532,7 +532,7 @@ impl InferenceEngine { .variable_types .contains_key(name.current_interned()) { - let new_name = ir::gensym(&name.original_name()); + let new_name = ir::gensym(name.original_name()); renames.insert(name.current_interned().clone(), new_name.clone()); new_name } else { -- 2.53.0 From 88d128df9f274ca1171b450d1e886d26f137c7fb Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Mon, 3 Jun 2024 20:36:31 -0700 Subject: [PATCH 55/59] CHECKPOINT: Everything builds again. --- build.rs | 17 +- src/backend.rs | 19 +- src/backend/error.rs | 5 +- src/backend/eval.rs | 10 +- src/backend/into_crane.rs | 63 +-- src/bin/ngrun.rs | 22 +- src/compiler.rs | 23 +- src/eval.rs | 17 +- src/eval/primtype.rs | 4 +- src/eval/value.rs | 16 +- src/ir/arbitrary.rs | 18 +- src/ir/ast.rs | 65 +-- src/ir/eval.rs | 49 +- src/ir/fields.rs | 20 +- src/ir/pretty.rs | 61 +- src/ir/strings.rs | 15 +- src/ir/top_level.rs | 28 +- src/lambda_lift.rs | 1 - src/lib.rs | 1 - src/repl.rs | 108 ++-- src/syntax.rs | 156 +++--- src/syntax/arbitrary.rs | 526 ++++++++++-------- src/syntax/ast.rs | 70 ++- src/syntax/eval.rs | 62 +-- src/{lambda_lift => syntax}/free_variables.rs | 36 +- src/syntax/location.rs | 36 ++ src/syntax/name.rs | 48 +- src/syntax/parser.lalrpop | 16 +- src/syntax/pretty.rs | 72 ++- src/syntax/replace_references.rs | 51 ++ src/syntax/validate.rs | 321 +++++++---- src/type_infer.rs | 33 +- src/type_infer/constraint.rs | 9 +- src/type_infer/convert.rs | 413 ++++++-------- src/type_infer/error.rs | 7 +- src/type_infer/finalize.rs | 65 ++- src/type_infer/solve.rs | 4 +- src/util.rs | 1 + src/util/warning_result.rs | 155 ++++++ 39 files changed, 1514 insertions(+), 1129 deletions(-) delete mode 100644 src/lambda_lift.rs rename src/{lambda_lift => syntax}/free_variables.rs (88%) create mode 100644 src/syntax/replace_references.rs create mode 100644 src/util/warning_result.rs diff --git a/build.rs b/build.rs index 266ae6b..00c1dba 100644 --- a/build.rs +++ b/build.rs @@ -45,17 +45,20 @@ fn generate_tests(f: &mut File, path_so_far: PathBuf) -> std::io::Result<()> { writeln!(f, " let mut file_database = SimpleFiles::new();")?; writeln!( f, - " let syntax = Syntax::parse_file(&mut file_database, {:?});", + " let syntax = crate::syntax::parse_file(&mut file_database, {:?});", entry.path().display() )?; if entry.path().to_string_lossy().contains("broken") { writeln!(f, " if syntax.is_err() {{")?; writeln!(f, " return;")?; writeln!(f, " }}")?; - writeln!(f, " let (errors, _) = syntax.unwrap().validate();")?; writeln!( f, - " assert_ne!(errors.len(), 0, \"should have seen an error\");" + " let mut validation_result = Syntax::validate(syntax.unwrap());" + )?; + writeln!( + f, + " assert!(validation_result.is_err(), \"should have seen an error\");" )?; } else { // NOTE: Since the advent of defaulting rules and type checking, we @@ -67,10 +70,14 @@ fn generate_tests(f: &mut File, path_so_far: PathBuf) -> std::io::Result<()> { f, " let syntax = syntax.expect(\"file should have parsed\");" )?; - writeln!(f, " let (errors, _) = syntax.validate();")?; + writeln!(f, " let validation_result = Syntax::validate(syntax);")?; writeln!( f, - " assert_eq!(errors.len(), 0, \"file should have no validation errors, but saw: {{:?}}\", errors);" + " assert!(validation_result.is_ok(), \"file should have no validation errors\");" + )?; + writeln!( + f, + " let syntax = validation_result.into_result().unwrap();" )?; writeln!(f, " let syntax_result = syntax.eval();")?; writeln!( diff --git a/src/backend.rs b/src/backend.rs index 2b475e4..4c642ee 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -33,7 +33,8 @@ mod runtime; pub use self::error::BackendError; pub use self::runtime::{RuntimeFunctionError, RuntimeFunctions}; -use crate::syntax::ConstantType; +use crate::ir::Name; +use crate::syntax::{ConstantType, Location}; use cranelift_codegen::entity::EntityRef; use cranelift_codegen::ir::types; use cranelift_codegen::settings::Configurable; @@ -41,7 +42,6 @@ use cranelift_codegen::{isa, settings}; use cranelift_jit::{JITBuilder, JITModule}; use cranelift_module::{default_libcall_names, DataDescription, DataId, FuncId, Linkage, Module}; use cranelift_object::{ObjectBuilder, ObjectModule}; -use internment::ArcIntern; use std::collections::HashMap; use target_lexicon::Triple; @@ -61,8 +61,8 @@ pub struct Backend { data_ctx: DataDescription, runtime_functions: RuntimeFunctions, defined_strings: HashMap, - defined_functions: HashMap, FuncId>, - defined_symbols: HashMap, (DataId, types::Type)>, + defined_functions: HashMap, + defined_symbols: HashMap, output_buffer: Option, platform: Triple, next_variable: usize, @@ -106,7 +106,7 @@ impl Backend { .module .declare_data(&alloc, Linkage::Import, true, false)?; retval.defined_symbols.insert( - ArcIntern::new(alloc), + Name::new(alloc, Location::manufactured()), (id, retval.module.target_config().pointer_type()), ); @@ -158,7 +158,7 @@ impl Backend { .module .declare_data(&alloc, Linkage::Import, true, false)?; retval.defined_symbols.insert( - ArcIntern::new(alloc), + Name::new(alloc, Location::manufactured()), (id, retval.module.target_config().pointer_type()), ); @@ -203,17 +203,16 @@ impl Backend { /// value will be null. pub fn define_variable( &mut self, - name: String, + name: Name, ctype: ConstantType, ) -> Result { self.data_ctx.define(Box::new(EMPTY_DATUM)); let id = self .module - .declare_data(&name, Linkage::Export, true, false)?; + .declare_data(name.current_name(), Linkage::Export, true, false)?; self.module.define_data(id, &self.data_ctx)?; self.data_ctx.clear(); - self.defined_symbols - .insert(ArcIntern::new(name), (id, ctype.into())); + self.defined_symbols.insert(name, (id, ctype.into())); Ok(id) } diff --git a/src/backend/error.rs b/src/backend/error.rs index 0fd0e7e..128dd59 100644 --- a/src/backend/error.rs +++ b/src/backend/error.rs @@ -1,4 +1,5 @@ -use crate::{backend::runtime::RuntimeFunctionError, ir::Type}; +use crate::backend::runtime::RuntimeFunctionError; +use crate::ir::{Name, Type}; use codespan_reporting::diagnostic::Diagnostic; use cranelift_codegen::{isa::LookupError, settings::SetError, CodegenError}; use cranelift_module::ModuleError; @@ -30,7 +31,7 @@ pub enum BackendError { #[error("Builtin function error: {0}")] BuiltinError(#[from] RuntimeFunctionError), #[error("Internal variable lookup error")] - VariableLookupFailure(ArcIntern), + VariableLookupFailure(Name), #[error(transparent)] CodegenError(#[from] CodegenError), #[error(transparent)] diff --git a/src/backend/eval.rs b/src/backend/eval.rs index 4aabe7e..097c34e 100644 --- a/src/backend/eval.rs +++ b/src/backend/eval.rs @@ -1,6 +1,7 @@ use crate::backend::Backend; use crate::eval::EvalError; -use crate::ir::{Expression, Program, Type}; +use crate::ir::{Expression, Name, Program, Type}; +use crate::syntax::Location; use cranelift_jit::JITModule; use cranelift_object::ObjectModule; #[cfg(test)] @@ -24,7 +25,10 @@ impl Backend { /// of the built-in test systems.) pub fn eval(program: Program) -> Result>> { let mut jitter = Backend::jit(Some(String::new()))?; - let function_id = jitter.compile_program("___test_jit_eval___", program)?; + let function_id = jitter.compile_program( + Name::new("___test_jit_eval___", Location::manufactured()), + program, + )?; jitter.module.finalize_definitions()?; let compiled_bytes = jitter.bytes(function_id); let compiled_function = unsafe { std::mem::transmute::<_, fn() -> ()>(compiled_bytes) }; @@ -58,7 +62,7 @@ impl Backend { let object_path = my_directory.path().join("object.o"); let executable_path = my_directory.path().join("test_executable"); - backend.compile_program("gogogo", program)?; + backend.compile_program(Name::new("gogogo", Location::manufactured()), program)?; let bytes = backend.bytes()?; std::fs::write(&object_path, bytes)?; Self::link(&object_path, &executable_path)?; diff --git a/src/backend/into_crane.rs b/src/backend/into_crane.rs index 68a7774..58267d0 100644 --- a/src/backend/into_crane.rs +++ b/src/backend/into_crane.rs @@ -1,7 +1,7 @@ use crate::backend::error::BackendError; use crate::backend::Backend; use crate::eval::PrimitiveType; -use crate::ir::{Expression, Primitive, Program, TopLevel, Type, Value, ValueOrRef, Variable}; +use crate::ir::{Expression, Name, Primitive, Program, Type, Value, ValueOrRef}; use crate::syntax::{ConstantType, Location}; use cranelift_codegen::ir::{ self, entities, types, AbiParam, Function, GlobalValue, InstBuilder, MemFlags, Signature, @@ -11,7 +11,6 @@ use cranelift_codegen::isa::CallConv; use cranelift_codegen::Context; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use cranelift_module::{DataDescription, FuncId, Linkage, Module}; -use internment::ArcIntern; use std::collections::{hash_map, HashMap}; const VOID_REPR_TYPE: types::Type = types::I64; @@ -60,17 +59,16 @@ impl Backend { /// are no such statements, the function will do nothing.) pub fn compile_program( &mut self, - function_name: &str, + function_name: Name, program: Program, ) -> Result { - let mut generated_body = vec![]; let mut variables = HashMap::new(); for (top_level_name, top_level_type) in program.get_top_level_variables() { match top_level_type { Type::Function(argument_types, return_type) => { let func_id = self.declare_function( - top_level_name.as_str(), + top_level_name.current_name(), Linkage::Export, argument_types, *return_type, @@ -80,7 +78,7 @@ impl Backend { Type::Primitive(pt) => { let data_id = self.module.declare_data( - top_level_name.as_str(), + top_level_name.current_name(), Linkage::Export, true, false, @@ -94,7 +92,7 @@ impl Backend { Type::Structure(mut fields) => { let data_id = self.module.declare_data( - top_level_name.as_str(), + top_level_name.current_name(), Linkage::Export, true, false, @@ -109,21 +107,24 @@ impl Backend { } let void = Type::Primitive(PrimitiveType::Void); - let main_func_id = - self.declare_function(function_name, Linkage::Export, vec![], void.clone())?; + let main_func_id = self.declare_function( + function_name.current_name(), + Linkage::Export, + vec![], + void.clone(), + )?; self.defined_functions - .insert(ArcIntern::new(function_name.to_string()), main_func_id); + .insert(function_name.clone(), main_func_id); - for item in program.items { - match item { - TopLevel::Function(name, args, rettype, body) => { - self.compile_function(&mut variables, name.as_str(), &args, rettype, body)?; - } - - TopLevel::Statement(stmt) => { - generated_body.push(stmt); - } - } + for (_, function) in program.functions.into_iter() { + let func_id = self.compile_function( + &mut variables, + function.name.clone(), + &function.arguments, + function.return_type, + function.body, + )?; + self.defined_functions.insert(function.name, func_id); } self.compile_function( @@ -131,7 +132,7 @@ impl Backend { function_name, &[], void.clone(), - Expression::Block(Location::manufactured(), void, generated_body), + program.body, ) } @@ -168,9 +169,9 @@ impl Backend { #[tracing::instrument(level = "debug", skip(self, variables, body))] pub fn compile_function( &mut self, - variables: &mut HashMap, - function_name: &str, - arguments: &[(Variable, Type)], + variables: &mut HashMap, + function_name: Name, + arguments: &[(Name, Type)], return_type: Type, body: Expression, ) -> Result { @@ -195,13 +196,12 @@ impl Backend { // return to the user. For now, we declare all functions defined by this // function as public/global/exported, although we may want to reconsider // this decision later. - let interned_name = ArcIntern::new(function_name.to_string()); - let func_id = match self.defined_functions.entry(interned_name) { + let func_id = match self.defined_functions.entry(function_name.clone()) { hash_map::Entry::Occupied(entry) => *entry.get(), hash_map::Entry::Vacant(vac) => { - tracing::warn!(name = ?function_name, "compiling undeclared function"); + tracing::warn!(name = ?function_name.current_name(), "compiling undeclared function"); let func_id = self.module.declare_function( - function_name, + function_name.current_name(), Linkage::Export, &basic_signature, )?; @@ -277,7 +277,7 @@ impl Backend { fn compile_expression( &mut self, expr: Expression, - variables: &mut HashMap, + variables: &mut HashMap, builder: &mut FunctionBuilder, ) -> Result<(entities::Value, types::Type), BackendError> { match expr { @@ -379,7 +379,8 @@ impl Backend { panic!("Got to backend with non-structure type in structure construction?!"); }; - let global_allocator = ArcIntern::new("__global_allocation_pointer__".to_string()); + let global_allocator = + Name::new("__global_allocation_pointer__", Location::manufactured()); let Some(ReferenceBuilder::Global(_, allocator_variable)) = variables.get(&global_allocator) else { @@ -635,7 +636,7 @@ impl Backend { fn compile_value_or_ref( &self, value_or_ref: ValueOrRef, - variables: &HashMap, + variables: &HashMap, builder: &mut FunctionBuilder, ) -> Result<(entities::Value, types::Type), BackendError> { match value_or_ref { diff --git a/src/bin/ngrun.rs b/src/bin/ngrun.rs index 0cc42ec..97cb4a2 100644 --- a/src/bin/ngrun.rs +++ b/src/bin/ngrun.rs @@ -2,7 +2,7 @@ use clap::Parser; use codespan_reporting::files::SimpleFiles; use ngr::backend::Backend; use ngr::eval::Value; -use ngr::syntax; +use ngr::syntax::{self, Location, Name}; use ngr::type_infer::TypeInferenceResult; use pretty::termcolor::StandardStream; use tracing_subscriber::prelude::*; @@ -36,7 +36,7 @@ fn print_result(result: (Value, String)) { fn jit(ir: ngr::ir::Program) -> Result { let mut backend = Backend::jit(None)?; - let function_id = backend.compile_program("gogogo", ir)?; + let function_id = backend.compile_program(Name::new("gogogo", Location::manufactured()), ir)?; backend.module.finalize_definitions()?; let compiled_bytes = backend.bytes(function_id); Ok(unsafe { std::mem::transmute::<_, fn() -> ()>(compiled_bytes) }) @@ -51,10 +51,11 @@ fn main() { let mut file_database = SimpleFiles::new(); let mut console = StandardStream::stdout(pretty::termcolor::ColorChoice::Auto); let console_options = codespan_reporting::term::Config::default(); - let syntax = syntax::Program::parse_file(&mut file_database, cli.file.as_ref()); + let syntax = syntax::parse_file(&mut file_database, cli.file.as_ref()); let mut emit = |x| { let _ = codespan_reporting::term::emit(&mut console, &console_options, &file_database, &x); }; + let syntax = match syntax { Ok(x) => x, Err(e) => { @@ -63,18 +64,17 @@ fn main() { } }; - let (errors, warnings) = syntax.validate(); - let stop = !errors.is_empty(); - for error in errors { - emit(error.into()); + let mut validation_result = syntax::Program::validate(syntax); + for item in validation_result.diagnostics() { + emit(item); } - for warning in warnings { - emit(warning.into()); - } - if stop { + if validation_result.is_err() { return; } + let syntax = validation_result + .into_result() + .expect("we already checked this"); if cli.interpreter == Interpreter::Syntax { match syntax.eval() { Err(e) => tracing::error!(error = %e, "Evaluation error"), diff --git a/src/compiler.rs b/src/compiler.rs index 8e8e5ec..2925842 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,5 +1,6 @@ -use crate::syntax::Program as Syntax; -use crate::{backend::Backend, type_infer::TypeInferenceResult}; +use crate::backend::Backend; +use crate::syntax::{Location, Name, Program as Syntax}; +use crate::type_infer::TypeInferenceResult; use codespan_reporting::{ diagnostic::Diagnostic, files::SimpleFiles, @@ -76,29 +77,23 @@ impl Compiler { fn compile_internal(&mut self, input_file: &str) -> Result>, CompilerError> { // Try to parse the file into our syntax AST. If we fail, emit the error // and then immediately return `None`. - let syntax = Syntax::parse_file(&mut self.file_database, input_file)?; + let raw_syntax = crate::syntax::parse_file(&mut self.file_database, input_file)?; // Now validate the user's syntax AST. This can possibly find errors and/or // create warnings. We can continue if we only get warnings, but need to stop // if we get any errors. - let (mut errors, mut warnings) = syntax.validate(); - let stop = !errors.is_empty(); - let messages = errors - .drain(..) - .map(Into::into) - .chain(warnings.drain(..).map(Into::into)); - + let mut validation_result = Syntax::validate(raw_syntax); // emit all the messages we receive; warnings *and* errors - for message in messages { + for message in validation_result.diagnostics() { self.emit(message); } // we got errors, so just stop right now. perhaps oddly, this is Ok(None); // we've already said all we're going to say in the messags above, so there's // no need to provide another `Err` result. - if stop { + let Some(syntax) = validation_result.into_result() else { return Ok(None); - } + }; // Now that we've validated it, let's do type inference, potentially turning // into IR while we're at it. @@ -136,7 +131,7 @@ impl Compiler { // Finally, send all this to Cranelift for conversion into an object file. let mut backend = Backend::object_file(Triple::host())?; let unknown = "".to_string(); - backend.compile_program("gogogo", ir)?; + backend.compile_program(Name::new("gogogo", Location::manufactured()), ir)?; for (_, decl) in backend.module.declarations().get_functions() { tracing::debug!(name = %decl.name.as_ref().unwrap_or(&unknown), linkage = ?decl.linkage, "function definition"); diff --git a/src/eval.rs b/src/eval.rs index daa2b4b..5abf096 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -38,8 +38,8 @@ mod primop; mod primtype; mod value; +use crate::syntax::Name; use cranelift_module::ModuleError; -use internment::ArcIntern; pub use primop::PrimOpError; pub use primtype::PrimitiveType; pub use value::Value; @@ -80,20 +80,11 @@ pub enum EvalError { #[error("Attempted to call something that wasn't a function at {0:?} (it was a {1})")] NotAFunction(crate::syntax::Location, Value), #[error("Wrong argument call for function ({1:?}) at {0:?}; expected {2}, saw {3}")] - WrongArgCount( - crate::syntax::Location, - Option>, - usize, - usize, - ), + WrongArgCount(crate::syntax::Location, Option, usize, usize), #[error("Value has no fields {1} (attempt to get field {2} at {0:?})")] - NoFieldForValue(crate::syntax::Location, Value, ArcIntern), + NoFieldForValue(crate::syntax::Location, Value, Name), #[error("Bad field {2} for structure {1:?} at {0:?}")] - BadFieldForStructure( - crate::syntax::Location, - Option>, - ArcIntern, - ), + BadFieldForStructure(crate::syntax::Location, Option, Name), } impl PartialEq> for EvalError { diff --git a/src/eval/primtype.rs b/src/eval/primtype.rs index c714627..6f48d5e 100644 --- a/src/eval/primtype.rs +++ b/src/eval/primtype.rs @@ -72,10 +72,10 @@ impl<'a, IR> TryFrom<&'a Value> for PrimitiveType { // not sure this is the right call Value::Number(_) => Ok(PrimitiveType::U64), Value::Closure(name, _, _, _) => Err(ValuePrimitiveTypeError::CannotConvertFunction( - name.as_ref().map(|x| (**x).clone()), + name.as_ref().map(|x| x.current_name().to_string()), )), Value::Structure(name, _) => Err(ValuePrimitiveTypeError::CannotConvertStructure( - name.as_ref().map(|x| (**x).clone()), + name.as_ref().map(|x| x.current_name().to_string()), )), Value::Primitive(prim) => Err(ValuePrimitiveTypeError::CannotConvertPrimitive( prim.clone(), diff --git a/src/eval/value.rs b/src/eval/value.rs index 99e8248..8a574bc 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -1,5 +1,5 @@ +use crate::syntax::Name; use crate::util::scoped_map::ScopedMap; -use internment::ArcIntern; use std::collections::HashMap; use std::fmt; @@ -21,16 +21,8 @@ pub enum Value { U64(u64), // a number of unknown type Number(u64), - Closure( - Option>, - ScopedMap, Value>, - Vec>, - IR, - ), - Structure( - Option>, - HashMap, Value>, - ), + Closure(Option, ScopedMap>, Vec, IR), + Structure(Option, HashMap>), Primitive(String), } @@ -85,7 +77,7 @@ fn format_value(value: &Value, f: &mut fmt::Formatter<'_>) -> fmt::Resul Value::Closure(None, _, _, _) => write!(f, ""), Value::Structure(on, fields) => { if let Some(n) = on { - write!(f, "{}", n.as_str())?; + write!(f, "{}", n.current_name())?; } write!(f, "{{")?; for (n, v) in fields.iter() { diff --git a/src/ir/arbitrary.rs b/src/ir/arbitrary.rs index 182c43b..ce1aa24 100644 --- a/src/ir/arbitrary.rs +++ b/src/ir/arbitrary.rs @@ -1,6 +1,6 @@ use crate::eval::PrimitiveType; use crate::ir::{ - Expression, Primitive, Program, TopLevel, Type, TypeWithVoid, Value, ValueOrRef, Variable, + Expression, Name, Primitive, Program, TopLevel, Type, TypeWithVoid, Value, ValueOrRef, }; use crate::syntax::Location; use crate::util::scoped_map::ScopedMap; @@ -216,8 +216,9 @@ impl ProgramTree { } let current = Program { - items, + functions: HashMap::new(), type_definitions: HashMap::new(), + body: unimplemented!(), }; ProgramTree { _rng: rng, current } @@ -284,7 +285,7 @@ impl ExpressionTree { fn generate_random_expression( rng: &mut TestRng, - env: &mut ScopedMap, + env: &mut ScopedMap, ) -> Expression { match EXPRESSION_TYPE_FREQUENCIES[EXPRESSION_TYPE_DISTRIBUTION.sample(rng)].0 { ExpressionType::Atomic => Expression::Atomic(generate_random_valueref(rng, env, None)), @@ -401,10 +402,7 @@ fn generate_random_expression( } } -fn generate_random_binding( - rng: &mut TestRng, - env: &mut ScopedMap, -) -> Expression { +fn generate_random_binding(rng: &mut TestRng, env: &mut ScopedMap) -> Expression { let name = generate_random_name(rng); let expr = generate_random_expression(rng, env); let ty = expr.type_of(); @@ -414,7 +412,7 @@ fn generate_random_binding( fn generate_random_valueref( rng: &mut TestRng, - env: &mut ScopedMap, + env: &mut ScopedMap, target_type: Option, ) -> ValueOrRef { let mut bindings = env.bindings(); @@ -458,9 +456,9 @@ fn generate_random_valueref( } } -fn generate_random_name(rng: &mut TestRng) -> Variable { +fn generate_random_name(rng: &mut TestRng) -> Name { let start = rng.gen_range('a'..='z'); - crate::ir::gensym(&format!("{}", start)) + Name::gensym(start) } fn generate_random_argument_type(rng: &mut TestRng) -> Type { diff --git a/src/ir/ast.rs b/src/ir/ast.rs index bb88f4a..8920d69 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -1,37 +1,14 @@ use crate::eval::PrimitiveType; pub use crate::ir::fields::Fields; +pub use crate::syntax::Name; use crate::syntax::{ConstantType, Location}; -use internment::ArcIntern; use proptest::arbitrary::Arbitrary; use std::collections::HashMap; use std::convert::TryFrom; use std::str::FromStr; -use std::sync::atomic::AtomicUsize; use super::arbitrary::ProgramGenerator; -/// We're going to represent variables as interned strings. -/// -/// These should be fast enough for comparison that it's OK, since it's going to end up -/// being pretty much the pointer to the string. -pub type Variable = ArcIntern; - -/// Generate a new symbol that is guaranteed to be different from every other symbol -/// currently known. -/// -/// This function will use the provided string as a base name for the symbol, but -/// extend it with numbers and characters to make it unique. While technically you -/// could roll-over these symbols, you probably don't need to worry about it. -pub fn gensym(base: &str) -> Variable { - static COUNTER: AtomicUsize = AtomicUsize::new(0); - - ArcIntern::new(format!( - "{}<{}>", - base, - COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst) - )) -} - /// The representation of a program within our IR. For now, this is exactly one file. /// /// A program consists of a series of statements and functions. The statements should @@ -50,11 +27,20 @@ pub fn gensym(base: &str) -> Variable { /// it's easiest to just make it an argument. #[derive(Clone, Debug)] pub struct Program { - // For now, a program is just a vector of statements. In the future, we'll probably - // extend this to include a bunch of other information, but for now: just a list. - pub items: Vec>, + // The set of functions declared in this program. + pub functions: HashMap>, // The set of types declared in this program. - pub type_definitions: HashMap, Type>, + pub type_definitions: HashMap, + // The thing to evaluate in the end. + pub body: Expression, +} + +#[derive(Clone, Debug)] +pub struct FunctionDefinition { + pub name: Name, + pub arguments: Vec<(Name, Type)>, + pub return_type: Type, + pub body: Expression, } impl Arbitrary for Program { @@ -76,7 +62,7 @@ pub enum TopLevel { Statement(Expression), // FIXME: Is the return type actually necessary, given we can infer it from // the expression type? - Function(Variable, Vec<(Variable, Type)>, Type, Expression), + Function(Name, Vec<(Name, Type)>, Type, Expression), } impl TopLevel { @@ -108,16 +94,11 @@ impl TopLevel { pub enum Expression { Atomic(ValueOrRef), Cast(Location, Type, ValueOrRef), - Construct( - Location, - Type, - ArcIntern, - HashMap, ValueOrRef>, - ), - FieldRef(Location, Type, Type, ValueOrRef, ArcIntern), + Construct(Location, Type, Name, HashMap>), + FieldRef(Location, Type, Type, ValueOrRef, Name), Block(Location, Type, Vec>), Call(Location, Type, ValueOrRef, Vec>), - Bind(Location, Variable, Type, Box>), + Bind(Location, Name, Type, Box>), } impl Expression { @@ -191,7 +172,7 @@ impl FromStr for Primitive { #[derive(Clone, Debug)] pub enum ValueOrRef { Value(Location, Type, Value), - Ref(Location, Type, ArcIntern), + Ref(Location, Type, Name), Primitive(Location, Type, Primitive), } @@ -293,7 +274,7 @@ impl<'a> TryInto for &'a Type { #[derive(Clone, Debug, Eq, PartialEq)] pub enum TypeOrVar { Primitive(PrimitiveType), - Variable(Location, ArcIntern), + Variable(Location, Name), Function(Vec, Box), Structure(Fields), } @@ -322,12 +303,12 @@ impl TypeOrVar { /// of this function could potentially cause overflow problems, but you're going to have /// to try really hard (like, 2^64 times) to make that happen. pub fn new_located(loc: Location) -> Self { - TypeOrVar::Variable(loc, gensym("t")) + TypeOrVar::Variable(loc.clone(), Name::located_gensym(loc, "t")) } /// Try replacing the given type variable with the given type, returning true if anything /// was changed. - pub fn replace(&mut self, name: &ArcIntern, replace_with: &TypeOrVar) -> bool { + pub fn replace(&mut self, name: &Name, replace_with: &TypeOrVar) -> bool { match self { TypeOrVar::Variable(_, var_name) if name == var_name => { *self = replace_with.clone(); @@ -487,7 +468,7 @@ impl TryFrom for Type { #[test] fn struct_sizes_are_rational() { - assert_eq!(8, std::mem::size_of::>()); + assert_eq!(8, std::mem::size_of::()); assert_eq!(24, std::mem::size_of::()); assert_eq!(1, std::mem::size_of::()); assert_eq!(1, std::mem::size_of::()); diff --git a/src/ir/eval.rs b/src/ir/eval.rs index 9a42560..ce89490 100644 --- a/src/ir/eval.rs +++ b/src/ir/eval.rs @@ -1,6 +1,6 @@ -use super::{Type, ValueOrRef}; +use super::{FunctionDefinition, Type, ValueOrRef}; use crate::eval::{EvalError, Value}; -use crate::ir::{Expression, Program, TopLevel, Variable}; +use crate::ir::{Expression, Name, Program}; use crate::util::scoped_map::ScopedMap; use std::collections::HashMap; use std::fmt::Display; @@ -9,7 +9,8 @@ type IRValue = Value>; type IREvalError = EvalError>; pub struct Evaluator { - env: ScopedMap>, + env: ScopedMap>, + functions: HashMap>, stdout: String, } @@ -17,6 +18,7 @@ impl Default for Evaluator { fn default() -> Self { Evaluator { env: ScopedMap::new(), + functions: HashMap::new(), stdout: String::new(), } } @@ -32,30 +34,9 @@ where /// /// The print outs will be newline separated, with one print out per line. pub fn eval(mut self, program: Program) -> Result<(IRValue, String), IREvalError> { - let mut last_value = Value::Void; - - for stmt in program.items.into_iter() { - match stmt { - TopLevel::Function(name, args, _, body) => { - let closure = Value::Closure( - Some(name.clone()), - self.env.clone(), - args.iter().map(|(x, _)| x.clone()).collect(), - body.clone(), - ); - - self.env.insert(name.clone(), closure.clone()); - - last_value = closure; - } - - TopLevel::Statement(expr) => { - last_value = self.eval_expr(expr)?; - } - } - } - - Ok((last_value, self.stdout)) + self.functions.extend(program.functions); + let retval = self.eval_expr(program.body)?; + Ok((retval, self.stdout)) } /// Get the current output of the evaluated program. @@ -203,8 +184,11 @@ where #[test] fn two_plus_three() { - let input = crate::syntax::Program::parse(0, "x = 2 + 3; print x;").expect("parse works"); - let ir = input.type_infer().expect("test should be type-valid"); + let input = crate::syntax::parse_string(0, "x = 2 + 3; print x;").expect("parse works"); + let program = crate::syntax::Program::validate(input) + .into_result() + .unwrap(); + let ir = program.type_infer().expect("test should be type-valid"); let evaluator = Evaluator::default(); let (_, result) = evaluator.eval(ir).expect("runs successfully"); assert_eq!("x = 5u64\n", &result); @@ -213,8 +197,11 @@ fn two_plus_three() { #[test] fn lotsa_math() { let input = - crate::syntax::Program::parse(0, "x = 2 + 3 * 10 / 5 - 1; print x;").expect("parse works"); - let ir = input.type_infer().expect("test should be type-valid"); + crate::syntax::parse_string(0, "x = 2 + 3 * 10 / 5 - 1; print x;").expect("parse works"); + let program = crate::syntax::Program::validate(input) + .into_result() + .unwrap(); + let ir = program.type_infer().expect("test should be type-valid"); let evaluator = Evaluator::default(); let (_, result) = evaluator.eval(ir).expect("runs successfully"); assert_eq!("x = 7u64\n", &result); diff --git a/src/ir/fields.rs b/src/ir/fields.rs index 239f1cf..744056c 100644 --- a/src/ir/fields.rs +++ b/src/ir/fields.rs @@ -1,12 +1,12 @@ +use crate::syntax::Name; use cranelift_module::DataDescription; -use internment::ArcIntern; use std::fmt; #[derive(Clone)] pub struct Fields { ordering: FieldOrdering, total_size: usize, - fields: Vec<(ArcIntern, T)>, + fields: Vec<(Name, T)>, } impl PartialEq for Fields { @@ -48,12 +48,12 @@ impl Fields { self.ordering } - pub fn insert(&mut self, name: ArcIntern, t: T) { + pub fn insert(&mut self, name: Name, t: T) { self.total_size += 8; self.fields.push((name, t)); } - pub fn get(&self, name: &ArcIntern) -> Option<&T> { + pub fn get(&self, name: &Name) -> Option<&T> { for (n, res) in self.fields.iter() { if n == name { return Some(res); @@ -75,11 +75,11 @@ impl Fields { self.fields.len() } - pub fn has_field(&self, name: &ArcIntern) -> bool { + pub fn has_field(&self, name: &Name) -> bool { self.fields.iter().any(|(current, _)| current == name) } - pub fn remove_field(&mut self, name: &ArcIntern) -> Option { + pub fn remove_field(&mut self, name: &Name) -> Option { let mut field_index = None; for (idx, (current, _)) in self.fields.iter().enumerate() { @@ -92,11 +92,11 @@ impl Fields { field_index.map(|i| self.fields.remove(i).1) } - pub fn iter(&self) -> impl Iterator, &T)> { + pub fn iter(&self) -> impl Iterator { self.fields.iter().map(|(x, y)| (x, y)) } - pub fn field_names(&self) -> impl Iterator> { + pub fn field_names(&self) -> impl Iterator { self.fields.iter().map(|(n, _)| n) } @@ -115,7 +115,7 @@ impl Fields { cranelift_description } - pub fn field_type_and_offset(&self, field: &ArcIntern) -> Option<(&T, i32)> { + pub fn field_type_and_offset(&self, field: &Name) -> Option<(&T, i32)> { let mut offset = 0; for (current, ty) in self.fields.iter() { @@ -135,7 +135,7 @@ impl Fields { } impl IntoIterator for Fields { - type Item = (ArcIntern, T); + type Item = (Name, T); type IntoIter = std::vec::IntoIter; fn into_iter(self) -> Self::IntoIter { diff --git a/src/ir/pretty.rs b/src/ir/pretty.rs index ab55bc3..9c20e74 100644 --- a/src/ir/pretty.rs +++ b/src/ir/pretty.rs @@ -5,12 +5,55 @@ use pretty::{Arena, DocAllocator, DocBuilder}; impl Program { pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { - allocator - .intersperse( - self.items.iter().map(|x| x.pretty(allocator)), - allocator.line(), - ) - .align() + let mut result = allocator.nil(); + + for (name, ty) in self.type_definitions.iter() { + result = result + .append(allocator.text("type")) + .append(allocator.space()) + .append(allocator.text(name.current_name().to_string())) + .append(allocator.space()) + .append(allocator.text("=")) + .append(allocator.space()) + .append(ty.pretty(allocator)) + .append(allocator.hardline()); + } + + if !self.type_definitions.is_empty() { + result = result.append(allocator.hardline()); + } + + for function in self.functions.values() { + result = result + .append(allocator.text("function")) + .append(allocator.space()) + .append(allocator.text(function.name.current_name().to_string())) + .append(allocator.text("(")) + .append(allocator.intersperse( + function.arguments.iter().map(|(x, t)| { + allocator + .text(x.original_name().to_string()) + .append(allocator.text(":")) + .append(allocator.space()) + .append(t.pretty(allocator)) + }), + allocator.text(","), + )) + .append(allocator.text(")")) + .append(allocator.space()) + .append(allocator.text("->")) + .append(allocator.space()) + .append(function.return_type.pretty(allocator)) + .append(allocator.softline()) + .append(function.body.pretty(allocator)) + .append(allocator.hardline()); + } + + if !self.functions.is_empty() { + result = result.append(allocator.hardline()); + } + + result.append(self.body.pretty(allocator)) } } @@ -23,7 +66,7 @@ impl TopLevel { TopLevel::Function(name, args, _, expr) => allocator .text("function") .append(allocator.space()) - .append(allocator.text(name.as_ref().to_string())) + .append(allocator.text(name.current_name().to_string())) .append(allocator.space()) .append( allocator @@ -104,7 +147,7 @@ impl Expression { } }, Expression::Bind(_, var, ty, expr) => allocator - .text(var.as_ref().to_string()) + .text(var.current_name().to_string()) .append(allocator.space()) .append(allocator.text(":")) .append(allocator.space()) @@ -134,7 +177,7 @@ impl ValueOrRef { pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { match self { ValueOrRef::Value(_, _, v) => v.pretty(allocator), - ValueOrRef::Ref(_, _, v) => allocator.text(v.as_ref().to_string()), + ValueOrRef::Ref(_, _, v) => allocator.text(v.current_name().to_string()), ValueOrRef::Primitive(_, _, p) => p.pretty(allocator), } } diff --git a/src/ir/strings.rs b/src/ir/strings.rs index 5d6d07f..27540b0 100644 --- a/src/ir/strings.rs +++ b/src/ir/strings.rs @@ -1,4 +1,4 @@ -use super::ast::{Expression, Program, TopLevel}; +use super::ast::{Expression, Program}; use internment::ArcIntern; use std::collections::HashSet; @@ -10,23 +10,14 @@ impl Program { pub fn strings(&self) -> HashSet> { let mut result = HashSet::new(); - for stmt in self.items.iter() { - stmt.register_strings(&mut result); + for function in self.functions.values() { + function.body.register_strings(&mut result); } result } } -impl TopLevel { - fn register_strings(&self, string_set: &mut HashSet>) { - match self { - TopLevel::Function(_, _, _, body) => body.register_strings(string_set), - TopLevel::Statement(stmt) => stmt.register_strings(string_set), - } - } -} - impl Expression { fn register_strings(&self, _string_set: &mut HashSet>) { // nothing has a string in here, at the moment diff --git a/src/ir/top_level.rs b/src/ir/top_level.rs index 728472d..7706112 100644 --- a/src/ir/top_level.rs +++ b/src/ir/top_level.rs @@ -1,38 +1,18 @@ -use crate::ir::{Expression, Program, TopLevel, TypeWithFunction, TypeWithVoid, Variable}; +use crate::ir::{Expression, Name, Program, TypeWithFunction, TypeWithVoid}; use std::collections::HashMap; impl Program { /// Retrieve the complete set of variables that are defined at the top level of /// this program. - pub fn get_top_level_variables(&self) -> HashMap { - let mut result = HashMap::new(); - - for item in self.items.iter() { - result.extend(item.get_top_level_variables()); - } - - result - } -} - -impl TopLevel { - /// Retrieve the complete set of variables that are defined at the top level of - /// this top-level item. - /// - /// For functions, this is the function name. For expressions this can be a little - /// bit more complicated, as it sort of depends on the block structuring. - pub fn get_top_level_variables(&self) -> HashMap { - match self { - TopLevel::Function(name, _, _, _) => HashMap::from([(name.clone(), self.type_of())]), - TopLevel::Statement(expr) => expr.get_top_level_variables(), - } + pub fn get_top_level_variables(&self) -> HashMap { + self.body.get_top_level_variables() } } impl Expression { /// Retrieve the complete set of variables that are defined at the top level of /// this expression. Basically, returns the variable named in bind. - pub fn get_top_level_variables(&self) -> HashMap { + pub fn get_top_level_variables(&self) -> HashMap { match self { Expression::Bind(_, name, ty, expr) => { let mut tlvs = expr.get_top_level_variables(); diff --git a/src/lambda_lift.rs b/src/lambda_lift.rs deleted file mode 100644 index 339b1c0..0000000 --- a/src/lambda_lift.rs +++ /dev/null @@ -1 +0,0 @@ -mod free_variables; diff --git a/src/lib.rs b/src/lib.rs index fcb21f6..23f924c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,7 +66,6 @@ pub mod eval; #[cfg(test)] mod examples; pub mod ir; -pub mod lambda_lift; pub mod syntax; pub mod type_infer; pub mod util; diff --git a/src/repl.rs b/src/repl.rs index 473dfa4..5daacd1 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -1,5 +1,5 @@ use crate::backend::{Backend, BackendError}; -use crate::syntax::{ConstantType, Expression, Location, ParserError, TopLevel}; +use crate::syntax::{ConstantType, Expression, Location, Name, ParserError, Program, TopLevel}; use crate::type_infer::TypeInferenceResult; use crate::util::scoped_map::ScopedMap; use codespan_reporting::diagnostic::Diagnostic; @@ -128,86 +128,76 @@ impl REPL { .expect("entry exists") .source(); let syntax = TopLevel::parse(entry, source)?; - - let program = match syntax { + let top_levels = match syntax { TopLevel::Expression(Expression::Binding(loc, name, expr)) => { // if this is a variable binding, and we've never defined this variable before, // we should tell cranelift about it. this is optimistic; if we fail to compile, // then we won't use this definition until someone tries again. - if !self.variable_binding_sites.contains_key(&name.current_name().to_string()) { + if !self + .variable_binding_sites + .contains_key(&name.current_name().to_string()) + { self.jitter.define_string(name.current_name())?; self.jitter - .define_variable(name.to_string(), ConstantType::U64)?; + .define_variable(name.clone(), ConstantType::U64)?; } - crate::syntax::Program { - items: vec![ - TopLevel::Expression(Expression::Binding(loc.clone(), name.clone(), expr)), - TopLevel::Expression(Expression::Call( + vec![ + TopLevel::Expression(Expression::Binding(loc.clone(), name.clone(), expr)), + TopLevel::Expression(Expression::Call( + loc.clone(), + Box::new(Expression::Primitive( loc.clone(), - Box::new(Expression::Primitive( - loc.clone(), - crate::syntax::Name::manufactured("print"), - )), - vec![Expression::Reference(name.clone())], + crate::syntax::Name::manufactured("print"), )), - ], - } + vec![Expression::Reference(name.clone())], + )), + ] } - x => crate::syntax::Program { items: vec![x] }, + x => vec![x], }; - - let (mut errors, mut warnings) = - program.validate_with_bindings(&mut self.variable_binding_sites); - let stop = !errors.is_empty(); - let messages = errors - .drain(..) - .map(Into::into) - .chain(warnings.drain(..).map(Into::into)); - - for message in messages { + let mut validation_result = + Program::validate_with_bindings(top_levels, &mut self.variable_binding_sites); + for message in validation_result.diagnostics() { self.emit_diagnostic(message)?; } - if stop { - return Ok(()); - } + if let Some(program) = validation_result.into_result() { + match program.type_infer() { + TypeInferenceResult::Failure { + mut errors, + mut warnings, + } => { + let messages = errors + .drain(..) + .map(Into::into) + .chain(warnings.drain(..).map(Into::into)); - match program.type_infer() { - TypeInferenceResult::Failure { - mut errors, - mut warnings, - } => { - let messages = errors - .drain(..) - .map(Into::into) - .chain(warnings.drain(..).map(Into::into)); - - for message in messages { - self.emit_diagnostic(message)?; + for message in messages { + self.emit_diagnostic(message)?; + } } - Ok(()) - } + TypeInferenceResult::Success { + result, + mut warnings, + } => { + for message in warnings.drain(..).map(Into::into) { + self.emit_diagnostic(message)?; + } - TypeInferenceResult::Success { - result, - mut warnings, - } => { - for message in warnings.drain(..).map(Into::into) { - self.emit_diagnostic(message)?; + let name = Name::new(format!("line{}", line_no), Location::manufactured()); + let function_id = self.jitter.compile_program(name, result)?; + self.jitter.module.finalize_definitions()?; + let compiled_bytes = self.jitter.bytes(function_id); + let compiled_function = + unsafe { std::mem::transmute::<_, fn() -> ()>(compiled_bytes) }; + compiled_function(); } - - let name = format!("line{}", line_no); - let function_id = self.jitter.compile_program(&name, result)?; - self.jitter.module.finalize_definitions()?; - let compiled_bytes = self.jitter.bytes(function_id); - let compiled_function = - unsafe { std::mem::transmute::<_, fn() -> ()>(compiled_bytes) }; - compiled_function(); - Ok(()) } } + + Ok(()) } } diff --git a/src/syntax.rs b/src/syntax.rs index 7e54c31..15ee23a 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -30,8 +30,10 @@ use logos::Logos; pub mod arbitrary; mod ast; pub mod eval; +mod free_variables; mod location; mod name; +mod replace_references; mod tokens; lalrpop_mod!( #[allow(clippy::just_underscores_and_digits, clippy::clone_on_copy)] @@ -41,19 +43,13 @@ lalrpop_mod!( pub mod pretty; mod validate; -#[cfg(test)] -use crate::syntax::arbitrary::GenerationEnvironment; pub use crate::syntax::ast::*; pub use crate::syntax::location::Location; pub use crate::syntax::name::Name; -pub use crate::syntax::parser::{ProgramParser, TopLevelParser, ExpressionParser}; +pub use crate::syntax::parser::{ExpressionParser, ProgramParser, TopLevelParser}; pub use crate::syntax::tokens::{LexerError, Token}; use lalrpop_util::ParseError; -#[cfg(test)] -use proptest::{arbitrary::Arbitrary, prop_assert}; use std::ops::Range; -#[cfg(test)] -use std::str::FromStr; use thiserror::Error; /// One of the many errors that can occur when processing text input. @@ -206,38 +202,36 @@ impl<'a> From<&'a ParserError> for Diagnostic { } } -impl Program { - /// Parse the given file, adding it to the database as part of the process. - /// - /// This operation reads the file from disk and adds it to the database for future - /// reference. If you get an error, we strongly suggest conversion to [`Diagnostic`] - /// and then reporting it to the user via [`codespan_reporting`]. You should use - /// this function if you're pretty sure that you've never seen this file before, - /// and [`Program::parse`] if you have and know its index and already have it in - /// memory. - pub fn parse_file( - file_database: &mut SimpleFiles, - file_name: &str, - ) -> Result { - let file_contents = std::fs::read_to_string(file_name)?; - let file_handle = file_database.add(file_name.to_string(), file_contents); - let file_db_info = file_database.get(file_handle)?; - Program::parse(file_handle, file_db_info.source()) - } +/// Parse the given file, adding it to the database as part of the process. +/// +/// This operation reads the file from disk and adds it to the database for future +/// reference. If you get an error, we strongly suggest conversion to [`Diagnostic`] +/// and then reporting it to the user via [`codespan_reporting`]. You should use +/// this function if you're pretty sure that you've never seen this file before, +/// and [`Program::parse`] if you have and know its index and already have it in +/// memory. +pub fn parse_file( + file_database: &mut SimpleFiles, + file_name: &str, +) -> Result, ParserError> { + let file_contents = std::fs::read_to_string(file_name)?; + let file_handle = file_database.add(file_name.to_string(), file_contents); + let file_db_info = file_database.get(file_handle)?; + parse_string(file_handle, file_db_info.source()) +} - /// Parse a block of text you have in memory, using the given index for [`Location`]s. - /// - /// If you use a nonsensical file index, everything will work fine until you try to - /// report an error, at which point [`codespan_reporting`] may have some nasty things - /// to say to you. - pub fn parse(file_idx: usize, buffer: &str) -> Result { - let lexer = Token::lexer(buffer) - .spanned() - .map(|x| permute_lexer_result(file_idx, x)); - ProgramParser::new() - .parse(file_idx, lexer) - .map_err(|e| ParserError::convert(file_idx, e)) - } +/// Parse a block of text you have in memory, using the given index for [`Location`]s. +/// +/// If you use a nonsensical file index, everything will work fine until you try to +/// report an error, at which point [`codespan_reporting`] may have some nasty things +/// to say to you. +pub fn parse_string(file_idx: usize, buffer: &str) -> Result, ParserError> { + let lexer = Token::lexer(buffer) + .spanned() + .map(|x| permute_lexer_result(file_idx, x)); + ProgramParser::new() + .parse(file_idx, lexer) + .map_err(|e| ParserError::convert(file_idx, e)) } impl TopLevel { @@ -286,70 +280,58 @@ fn permute_lexer_result( } } -#[cfg(test)] -impl FromStr for Program { - type Err = ParserError; - - fn from_str(s: &str) -> Result { - Program::parse(0, s) - } -} - #[test] fn order_of_operations() { let muladd1 = "x = 1 + 2 * 3;"; let testfile = 0; assert_eq!( - Program::from_str(muladd1).unwrap(), - Program { - items: vec![TopLevel::Expression(Expression::Binding( - Location::new(testfile, 0..1), - Name::manufactured("x"), - Box::new(Expression::Call( + parse_string(0, muladd1).unwrap(), + vec![TopLevel::Expression(Expression::Binding( + Location::new(testfile, 0..1), + Name::manufactured("x"), + Box::new(Expression::Call( + Location::new(testfile, 6..7), + Box::new(Expression::Primitive( Location::new(testfile, 6..7), - Box::new(Expression::Primitive( - Location::new(testfile, 6..7), - Name::manufactured("+") - )), - vec![ - Expression::Value( - Location::new(testfile, 4..5), - Value::Number(None, None, 1), - ), - Expression::Call( + Name::manufactured("+") + )), + vec![ + Expression::Value(Location::new(testfile, 4..5), Value::Number(None, None, 1),), + Expression::Call( + Location::new(testfile, 10..11), + Box::new(Expression::Primitive( Location::new(testfile, 10..11), - Box::new(Expression::Primitive( - Location::new(testfile, 10..11), - Name::manufactured("*") - )), - vec![ - Expression::Value( - Location::new(testfile, 8..9), - Value::Number(None, None, 2), - ), - Expression::Value( - Location::new(testfile, 12..13), - Value::Number(None, None, 3), - ), - ] - ) - ] - )) - ))], - } + Name::manufactured("*") + )), + vec![ + Expression::Value( + Location::new(testfile, 8..9), + Value::Number(None, None, 2), + ), + Expression::Value( + Location::new(testfile, 12..13), + Value::Number(None, None, 3), + ), + ] + ) + ] + )) + ))], ); } proptest::proptest! { #[test] - fn random_syntaxes_validate(program: Program) { - let (errors, _) = program.validate(); - prop_assert!(errors.is_empty()); + fn random_syntaxes_validate(program in self::arbitrary::ProgramGenerator::default()) { + let result = Program::validate(program); + proptest::prop_assert!(result.is_ok()); } #[test] - fn generated_run_or_overflow(program in Program::arbitrary_with(GenerationEnvironment::new(false))) { + fn generated_run_or_overflow(program in self::arbitrary::ProgramGenerator::default()) { use crate::eval::{EvalError, PrimOpError}; - prop_assert!(matches!(program.eval(), Ok(_) | Err(EvalError::PrimOp(PrimOpError::MathFailure(_))))); + let validated = Program::validate(program); + let actual_program = validated.into_result().expect("got a valid result"); + proptest::prop_assert!(matches!(actual_program.eval(), Ok(_) | Err(EvalError::PrimOp(PrimOpError::MathFailure(_))))); } } diff --git a/src/syntax/arbitrary.rs b/src/syntax/arbitrary.rs index c87d941..1d489a7 100644 --- a/src/syntax/arbitrary.rs +++ b/src/syntax/arbitrary.rs @@ -1,7 +1,9 @@ use crate::syntax::ast::{ConstantType, Expression, Program, TopLevel, Value}; -use crate::syntax::name::Name; use crate::syntax::location::Location; +use crate::syntax::name::Name; use proptest::sample::select; +use proptest::strategy::{NewTree, ValueTree}; +use proptest::test_runner::{TestRng, TestRunner}; use proptest::{ prelude::{Arbitrary, BoxedStrategy, Strategy}, strategy::{Just, Union}, @@ -11,248 +13,296 @@ use std::ops::Range; pub const VALID_VARIABLE_NAMES: &str = r"[a-z][a-zA-Z0-9_]*"; -impl ConstantType { - fn get_operators(&self) -> &'static [(&'static str, usize)] { - match self { - ConstantType::Void => &[], - ConstantType::I8 | ConstantType::I16 | ConstantType::I32 | ConstantType::I64 => { - &[("+", 2), ("negate", 1), ("-", 2), ("*", 2), ("/", 2)] - } - ConstantType::U8 | ConstantType::U16 | ConstantType::U32 | ConstantType::U64 => { - &[("+", 2), ("-", 2), ("*", 2), ("/", 2)] - } +#[derive(Debug, Default)] +pub struct ProgramGenerator {} + +impl Strategy for ProgramGenerator { + type Tree = ProgramTree; + type Value = Vec; + + fn new_tree(&self, runner: &mut TestRunner) -> NewTree { + unimplemented!() + } +} + +pub struct ProgramTree { + _rng: TestRng, + current: Vec, +} + +impl ProgramTree { + fn new(mut rng: TestRng) -> Self { + ProgramTree { + _rng: rng, + current: vec![], } } } -#[derive(Clone)] -pub struct GenerationEnvironment { - allow_inference: bool, - block_length: Range, - bindings: HashMap, - return_type: ConstantType, -} +impl ValueTree for ProgramTree { + type Value = Vec; -impl Default for GenerationEnvironment { - fn default() -> Self { - GenerationEnvironment { - allow_inference: true, - block_length: 2..10, - bindings: HashMap::new(), - return_type: ConstantType::U64, - } + fn current(&self) -> Self::Value { + self.current.clone() + } + + fn simplify(&mut self) -> bool { + false + } + + fn complicate(&mut self) -> bool { + false } } -impl GenerationEnvironment { - pub fn new(allow_inference: bool) -> Self { - GenerationEnvironment { - allow_inference, - ..Default::default() - } - } -} - -impl Arbitrary for Program { - type Parameters = GenerationEnvironment; - type Strategy = BoxedStrategy; - - fn arbitrary_with(genenv: Self::Parameters) -> Self::Strategy { - proptest::collection::vec( - ProgramTopLevelInfo::arbitrary(), - genenv.block_length.clone(), - ) - .prop_flat_map(move |mut ptlis| { - let mut items = Vec::new(); - let mut genenv = genenv.clone(); - - for psi in ptlis.drain(..) { - if genenv.bindings.is_empty() || psi.should_be_binding { - genenv.return_type = psi.binding_type; - let expr = Expression::arbitrary_with(genenv.clone()); - genenv.bindings.insert(psi.name.clone(), psi.binding_type); - items.push( - expr.prop_map(move |expr| { - TopLevel::Expression(Expression::Binding( - Location::manufactured(), - psi.name.clone(), - Box::new(expr), - )) - }) - .boxed(), - ); - } else { - let printers = genenv.bindings.keys().map(|n| { - Just(TopLevel::Expression(Expression::Call( - Location::manufactured(), - Box::new(Expression::Primitive( - Location::manufactured(), - Name::manufactured("print"), - )), - vec![Expression::Reference(n.clone())], - ))) - }); - items.push(Union::new(printers).boxed()); - } - } - - items.prop_map(|items| Program { items }).boxed() - }) - .boxed() - } -} - -impl Arbitrary for Name { - type Parameters = (); - type Strategy = BoxedStrategy; - - fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { - VALID_VARIABLE_NAMES.prop_map(Name::manufactured).boxed() - } -} - -#[derive(Debug)] -struct ProgramTopLevelInfo { - should_be_binding: bool, - name: Name, - binding_type: ConstantType, -} - -impl Arbitrary for ProgramTopLevelInfo { - type Parameters = (); - type Strategy = BoxedStrategy; - - fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - ( - Union::new(vec![Just(true), Just(true), Just(false)]), - Name::arbitrary(), - ConstantType::arbitrary(), - ) - .prop_map( - |(should_be_binding, name, binding_type)| ProgramTopLevelInfo { - should_be_binding, - name, - binding_type, - }, - ) - .boxed() - } -} - -impl Arbitrary for Expression { - type Parameters = GenerationEnvironment; - type Strategy = BoxedStrategy; - - fn arbitrary_with(genenv: Self::Parameters) -> Self::Strategy { - // Value(Location, Value). These are the easiest variations to create, because we can always - // create one. - let value_strategy = Value::arbitrary_with(genenv.clone()) - .prop_map(|x| Expression::Value(Location::manufactured(), x)) - .boxed(); - - // Reference(Location, String), These are slightly trickier, because we can end up in a situation - // where either no variables are defined, or where none of the defined variables have a type we - // can work with. So what we're going to do is combine this one with the previous one as a "leaf - // strategy" -- our non-recursive items -- if we can, or just set that to be the value strategy - // if we can't actually create an references. - let mut bound_variables_of_type = genenv - .bindings - .iter() - .filter(|(_, v)| genenv.return_type == **v) - .map(|(n, _)| n) - .collect::>(); - let leaf_strategy = if bound_variables_of_type.is_empty() { - value_strategy - } else { - let mut strats = bound_variables_of_type - .drain(..) - .map(|x| { Just(Expression::Reference(x.clone())).boxed() - }) - .collect::>(); - strats.push(value_strategy); - Union::new(strats).boxed() - }; - - // now we generate our recursive types, given our leaf strategy - leaf_strategy - .prop_recursive(3, 10, 2, move |strat| { - ( - select(genenv.return_type.get_operators()), - strat.clone(), - strat, - ) - .prop_map(|((oper, count), left, right)| { - let mut args = vec![left, right]; - while args.len() > count { - args.pop(); - } - Expression::Call( - Location::manufactured(), - Box::new(Expression::Primitive( - Location::manufactured(), - Name::manufactured(oper), - )), - args, - ) - }) - }) - .boxed() - } -} - -impl Arbitrary for Value { - type Parameters = GenerationEnvironment; - type Strategy = BoxedStrategy; - - fn arbitrary_with(genenv: Self::Parameters) -> Self::Strategy { - let printed_base_strategy = Union::new([ - Just(None::), - Just(Some(2)), - Just(Some(8)), - Just(Some(10)), - Just(Some(16)), - ]); - let value_strategy = u64::arbitrary(); - - (printed_base_strategy, bool::arbitrary(), value_strategy) - .prop_map(move |(base, declare_type, value)| { - let converted_value = match genenv.return_type { - ConstantType::Void => value, - ConstantType::I8 => value % (i8::MAX as u64), - ConstantType::U8 => value % (u8::MAX as u64), - ConstantType::I16 => value % (i16::MAX as u64), - ConstantType::U16 => value % (u16::MAX as u64), - ConstantType::I32 => value % (i32::MAX as u64), - ConstantType::U32 => value % (u32::MAX as u64), - ConstantType::I64 => value % (i64::MAX as u64), - ConstantType::U64 => value, - }; - let ty = if declare_type || !genenv.allow_inference { - Some(genenv.return_type) - } else { - None - }; - Value::Number(base, ty, converted_value) - }) - .boxed() - } -} - -impl Arbitrary for ConstantType { - type Parameters = (); - type Strategy = BoxedStrategy; - - fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { - Union::new([ - Just(ConstantType::I8), - Just(ConstantType::I16), - Just(ConstantType::I32), - Just(ConstantType::I64), - Just(ConstantType::U8), - Just(ConstantType::U16), - Just(ConstantType::U32), - Just(ConstantType::U64), - ]) - .boxed() - } -} +//impl ConstantType { +// fn get_operators(&self) -> &'static [(&'static str, usize)] { +// match self { +// ConstantType::Void => &[], +// ConstantType::I8 | ConstantType::I16 | ConstantType::I32 | ConstantType::I64 => { +// &[("+", 2), ("negate", 1), ("-", 2), ("*", 2), ("/", 2)] +// } +// ConstantType::U8 | ConstantType::U16 | ConstantType::U32 | ConstantType::U64 => { +// &[("+", 2), ("-", 2), ("*", 2), ("/", 2)] +// } +// } +// } +//} +// +//#[derive(Clone)] +//pub struct GenerationEnvironment { +// allow_inference: bool, +// block_length: Range, +// bindings: HashMap, +// return_type: ConstantType, +//} +// +//impl Default for GenerationEnvironment { +// fn default() -> Self { +// GenerationEnvironment { +// allow_inference: true, +// block_length: 2..10, +// bindings: HashMap::new(), +// return_type: ConstantType::U64, +// } +// } +//} +// +//impl GenerationEnvironment { +// pub fn new(allow_inference: bool) -> Self { +// GenerationEnvironment { +// allow_inference, +// ..Default::default() +// } +// } +//} +// +//impl Arbitrary for Program { +// type Parameters = GenerationEnvironment; +// type Strategy = BoxedStrategy; +// +// fn arbitrary_with(genenv: Self::Parameters) -> Self::Strategy { +// proptest::collection::vec( +// ProgramTopLevelInfo::arbitrary(), +// genenv.block_length.clone(), +// ) +// .prop_flat_map(move |mut ptlis| { +// let mut items = Vec::new(); +// let mut genenv = genenv.clone(); +// +// for psi in ptlis.drain(..) { +// if genenv.bindings.is_empty() || psi.should_be_binding { +// genenv.return_type = psi.binding_type; +// let expr = Expression::arbitrary_with(genenv.clone()); +// genenv.bindings.insert(psi.name.clone(), psi.binding_type); +// items.push( +// expr.prop_map(move |expr| { +// TopLevel::Expression(Expression::Binding( +// Location::manufactured(), +// psi.name.clone(), +// Box::new(expr), +// )) +// }) +// .boxed(), +// ); +// } else { +// let printers = genenv.bindings.keys().map(|n| { +// Just(TopLevel::Expression(Expression::Call( +// Location::manufactured(), +// Box::new(Expression::Primitive( +// Location::manufactured(), +// Name::manufactured("print"), +// )), +// vec![Expression::Reference(n.clone())], +// ))) +// }); +// items.push(Union::new(printers).boxed()); +// } +// } +// +// items +// .prop_map(|items| Program { +// functions: HashMap::new(), +// structures: HashMap::new(), +// body: unimplemented!(), +// }) +// .boxed() +// }) +// .boxed() +// } +//} +// +//impl Arbitrary for Name { +// type Parameters = (); +// type Strategy = BoxedStrategy; +// +// fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { +// VALID_VARIABLE_NAMES.prop_map(Name::manufactured).boxed() +// } +//} +// +//#[derive(Debug)] +//struct ProgramTopLevelInfo { +// should_be_binding: bool, +// name: Name, +// binding_type: ConstantType, +//} +// +//impl Arbitrary for ProgramTopLevelInfo { +// type Parameters = (); +// type Strategy = BoxedStrategy; +// +// fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { +// ( +// Union::new(vec![Just(true), Just(true), Just(false)]), +// Name::arbitrary(), +// ConstantType::arbitrary(), +// ) +// .prop_map( +// |(should_be_binding, name, binding_type)| ProgramTopLevelInfo { +// should_be_binding, +// name, +// binding_type, +// }, +// ) +// .boxed() +// } +//} +// +//impl Arbitrary for Expression { +// type Parameters = GenerationEnvironment; +// type Strategy = BoxedStrategy; +// +// fn arbitrary_with(genenv: Self::Parameters) -> Self::Strategy { +// // Value(Location, Value). These are the easiest variations to create, because we can always +// // create one. +// let value_strategy = Value::arbitrary_with(genenv.clone()) +// .prop_map(|x| Expression::Value(Location::manufactured(), x)) +// .boxed(); +// +// // Reference(Location, String), These are slightly trickier, because we can end up in a situation +// // where either no variables are defined, or where none of the defined variables have a type we +// // can work with. So what we're going to do is combine this one with the previous one as a "leaf +// // strategy" -- our non-recursive items -- if we can, or just set that to be the value strategy +// // if we can't actually create an references. +// let mut bound_variables_of_type = genenv +// .bindings +// .iter() +// .filter(|(_, v)| genenv.return_type == **v) +// .map(|(n, _)| n) +// .collect::>(); +// let leaf_strategy = if bound_variables_of_type.is_empty() { +// value_strategy +// } else { +// let mut strats = bound_variables_of_type +// .drain(..) +// .map(|x| Just(Expression::Reference(x.clone())).boxed()) +// .collect::>(); +// strats.push(value_strategy); +// Union::new(strats).boxed() +// }; +// +// // now we generate our recursive types, given our leaf strategy +// leaf_strategy +// .prop_recursive(3, 10, 2, move |strat| { +// ( +// select(genenv.return_type.get_operators()), +// strat.clone(), +// strat, +// ) +// .prop_map(|((oper, count), left, right)| { +// let mut args = vec![left, right]; +// while args.len() > count { +// args.pop(); +// } +// Expression::Call( +// Location::manufactured(), +// Box::new(Expression::Primitive( +// Location::manufactured(), +// Name::manufactured(oper), +// )), +// args, +// ) +// }) +// }) +// .boxed() +// } +//} +// +//impl Arbitrary for Value { +// type Parameters = GenerationEnvironment; +// type Strategy = BoxedStrategy; +// +// fn arbitrary_with(genenv: Self::Parameters) -> Self::Strategy { +// let printed_base_strategy = Union::new([ +// Just(None::), +// Just(Some(2)), +// Just(Some(8)), +// Just(Some(10)), +// Just(Some(16)), +// ]); +// let value_strategy = u64::arbitrary(); +// +// (printed_base_strategy, bool::arbitrary(), value_strategy) +// .prop_map(move |(base, declare_type, value)| { +// let converted_value = match genenv.return_type { +// ConstantType::Void => value, +// ConstantType::I8 => value % (i8::MAX as u64), +// ConstantType::U8 => value % (u8::MAX as u64), +// ConstantType::I16 => value % (i16::MAX as u64), +// ConstantType::U16 => value % (u16::MAX as u64), +// ConstantType::I32 => value % (i32::MAX as u64), +// ConstantType::U32 => value % (u32::MAX as u64), +// ConstantType::I64 => value % (i64::MAX as u64), +// ConstantType::U64 => value, +// }; +// let ty = if declare_type || !genenv.allow_inference { +// Some(genenv.return_type) +// } else { +// None +// }; +// Value::Number(base, ty, converted_value) +// }) +// .boxed() +// } +//} +// +//impl Arbitrary for ConstantType { +// type Parameters = (); +// type Strategy = BoxedStrategy; +// +// fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { +// Union::new([ +// Just(ConstantType::I8), +// Just(ConstantType::I16), +// Just(ConstantType::I32), +// Just(ConstantType::I64), +// Just(ConstantType::U8), +// Just(ConstantType::U16), +// Just(ConstantType::U32), +// Just(ConstantType::U64), +// ]) +// .boxed() +// } +//} +// diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index c0fa09f..7fb24e9 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -1,6 +1,9 @@ use crate::syntax::name::Name; -use crate::syntax::Location; pub use crate::syntax::tokens::ConstantType; +use crate::syntax::Location; +use std::collections::HashMap; + +use super::location::Located; /// A structure represented a parsed program. /// @@ -12,7 +15,57 @@ pub use crate::syntax::tokens::ConstantType; /// `validate` and it comes back without errors. #[derive(Clone, Debug, PartialEq)] pub struct Program { - pub items: Vec, + pub functions: HashMap, + pub structures: HashMap, + pub body: Expression, +} + +/// A function that we want to compile. +/// +/// Later, when we've done a lot of analysis, the `Option`s +/// will turn into concrete types. For now, though, we stick with +/// the surface syntax and leave them as optional. The name of the +/// function is intentionally duplicated, to make our life easier. +#[derive(Clone, Debug, PartialEq)] +pub struct FunctionDefinition { + pub name: Name, + pub arguments: Vec<(Name, Option)>, + pub return_type: Option, + pub body: Expression, +} + +impl FunctionDefinition { + pub fn new( + name: Name, + arguments: Vec<(Name, Option)>, + return_type: Option, + body: Expression, + ) -> Self { + FunctionDefinition { + name, + arguments, + return_type, + body, + } + } +} + +/// A structure type that we might want to reference in the future. +#[derive(Clone, Debug, PartialEq)] +pub struct StructureDefinition { + pub name: Name, + pub location: Location, + pub fields: Vec<(Name, Option)>, +} + +impl StructureDefinition { + pub fn new(location: Location, name: Name, fields: Vec<(Name, Option)>) -> Self { + StructureDefinition { + name, + location, + fields, + } + } } /// A thing that can sit at the top level of a file. @@ -26,6 +79,15 @@ pub enum TopLevel { Structure(Location, Name, Vec<(Name, Type)>), } +impl Located for TopLevel { + fn location(&self) -> &Location { + match self { + TopLevel::Expression(exp) => exp.location(), + TopLevel::Structure(loc, _, _) => loc, + } + } +} + /// An expression in the underlying syntax. /// /// Like statements, these expressions are guaranteed to have been @@ -114,9 +176,9 @@ impl PartialEq for Expression { } } -impl Expression { +impl Located for Expression { /// Get the location of the expression in the source file (if there is one). - pub fn location(&self) -> &Location { + fn location(&self) -> &Location { match self { Expression::Value(loc, _) => loc, Expression::Constructor(loc, _, _) => loc, diff --git a/src/syntax/eval.rs b/src/syntax/eval.rs index 58ac657..e07a876 100644 --- a/src/syntax/eval.rs +++ b/src/syntax/eval.rs @@ -1,7 +1,6 @@ use crate::eval::{EvalError, PrimitiveType, Value}; -use crate::syntax::{ConstantType, Expression, Program, TopLevel}; +use crate::syntax::{ConstantType, Expression, Name, Program}; use crate::util::scoped_map::ScopedMap; -use internment::ArcIntern; use std::collections::HashMap; use std::str::FromStr; @@ -20,19 +19,8 @@ impl Program { pub fn eval(&self) -> Result<(Value, String), EvalError> { let mut env = ScopedMap::new(); let mut stdout = String::new(); - let mut last_result = Value::Void; - - for stmt in self.items.iter() { - match stmt { - TopLevel::Expression(expr) => last_result = expr.eval(&mut stdout, &mut env)?, - - TopLevel::Structure(_, _, _) => { - last_result = Value::Void; - } - } - } - - Ok((last_result, stdout)) + let result = self.body.eval(&mut stdout, &mut env)?; + Ok((result, stdout)) } } @@ -40,7 +28,7 @@ impl Expression { fn eval( &self, stdout: &mut String, - env: &mut ScopedMap, Value>, + env: &mut ScopedMap>, ) -> Result, EvalError> { match self { Expression::Value(_, v) => match v { @@ -63,35 +51,37 @@ impl Expression { let mut map = HashMap::with_capacity(fields.len()); for (k, v) in fields.iter() { - map.insert(k.clone().intern(), v.eval(stdout, env)?); + map.insert(k.clone(), v.eval(stdout, env)?); } - Ok(Value::Structure(Some(on.clone().intern()), map)) + Ok(Value::Structure(Some(on.clone()), map)) } Expression::Reference(n) => env - .get(n.current_interned()) - .ok_or_else(|| EvalError::LookupFailed(n.location().clone(), n.current_name().to_string())) + .get(n) + .ok_or_else(|| { + EvalError::LookupFailed(n.location().clone(), n.current_name().to_string()) + }) .cloned(), Expression::FieldRef(loc, expr, field) => { let struck = expr.eval(stdout, env)?; if let Value::Structure(on, mut fields) = struck { - if let Some(value) = fields.remove(&field.clone().intern()) { + if let Some(value) = fields.remove(&field.clone()) { Ok(value) } else { Err(EvalError::BadFieldForStructure( loc.clone(), on, - field.clone().intern(), + field.clone(), )) } } else { Err(EvalError::NoFieldForValue( loc.clone(), struck, - field.clone().intern(), + field.clone(), )) } } @@ -130,8 +120,7 @@ impl Expression { Value::Primitive(name) if name == "print" => { if let [Expression::Reference(name)] = &args[..] { - let value = Expression::Reference(name.clone()) - .eval(stdout, env)?; + let value = Expression::Reference(name.clone()).eval(stdout, env)?; let value = match value { Value::Number(x) => Value::U64(x), x => x, @@ -172,24 +161,20 @@ impl Expression { Expression::Binding(_, name, value) => { let actual_value = value.eval(stdout, env)?; - env.insert(name.clone().intern(), actual_value.clone()); + env.insert(name.clone(), actual_value.clone()); Ok(actual_value) } Expression::Function(_, name, arg_names, _, body) => { let result = Value::Closure( - name.as_ref().map(|n| n.current_interned().clone()), + name.clone(), env.clone(), - arg_names - .iter() - .cloned() - .map(|(x, _)| x.current_interned().clone()) - .collect(), + arg_names.iter().cloned().map(|(x, _)| x.clone()).collect(), *body.clone(), ); if let Some(name) = name { - env.insert(name.clone().intern(), result.clone()); + env.insert(name.clone(), result.clone()); } Ok(result) @@ -200,14 +185,17 @@ impl Expression { #[test] fn two_plus_three() { - let input = Program::parse(0, "x = 2 + 3; print x;").expect("parse works"); - let (_, output) = input.eval().expect("runs successfully"); + let input = crate::syntax::parse_string(0, "x = 2 + 3; print x;").expect("parse works"); + let program = Program::validate(input).into_result().unwrap(); + let (_, output) = program.eval().expect("runs successfully"); assert_eq!("x = 5u64\n", &output); } #[test] fn lotsa_math() { - let input = Program::parse(0, "x = 2 + 3 * 10 / 5 - 1; print x;").expect("parse works"); - let (_, output) = input.eval().expect("runs successfully"); + let input = + crate::syntax::parse_string(0, "x = 2 + 3 * 10 / 5 - 1; print x;").expect("parse works"); + let program = Program::validate(input).into_result().unwrap(); + let (_, output) = program.eval().expect("runs successfully"); assert_eq!("x = 7u64\n", &output); } diff --git a/src/lambda_lift/free_variables.rs b/src/syntax/free_variables.rs similarity index 88% rename from src/lambda_lift/free_variables.rs rename to src/syntax/free_variables.rs index 2f8f868..1c53ac3 100644 --- a/src/lambda_lift/free_variables.rs +++ b/src/syntax/free_variables.rs @@ -9,22 +9,22 @@ impl Expression { pub fn free_variables(&self) -> HashSet { match self { Expression::Value(_, _) => HashSet::new(), - Expression::Constructor(_, _, args) => + Expression::Constructor(_, _, args) => { args.iter().fold(HashSet::new(), |mut existing, (_, expr)| { existing.extend(expr.free_variables()); existing - }), + }) + } Expression::Reference(n) => HashSet::from([n.clone()]), Expression::FieldRef(_, expr, _) => expr.free_variables(), Expression::Cast(_, _, expr) => expr.free_variables(), Expression::Primitive(_, _) => HashSet::new(), - Expression::Call(_, f, args) => - args.iter() - .fold(f.free_variables(), - |mut existing, expr| { - existing.extend(expr.free_variables()); - existing - }), + Expression::Call(_, f, args) => { + args.iter().fold(f.free_variables(), |mut existing, expr| { + existing.extend(expr.free_variables()); + existing + }) + } Expression::Block(_, exprs) => { let mut free_vars = HashSet::new(); let mut bound_vars = HashSet::new(); @@ -64,22 +64,22 @@ impl Expression { pub fn new_bindings(&self) -> HashSet { match self { Expression::Value(_, _) => HashSet::new(), - Expression::Constructor(_, _, args) => + Expression::Constructor(_, _, args) => { args.iter().fold(HashSet::new(), |mut existing, (_, expr)| { existing.extend(expr.new_bindings()); existing - }), + }) + } Expression::Reference(_) => HashSet::new(), Expression::FieldRef(_, expr, _) => expr.new_bindings(), Expression::Cast(_, _, expr) => expr.new_bindings(), Expression::Primitive(_, _) => HashSet::new(), - Expression::Call(_, f, args) => - args.iter() - .fold(f.new_bindings(), - |mut existing, expr| { - existing.extend(expr.new_bindings()); - existing - }), + Expression::Call(_, f, args) => { + args.iter().fold(f.new_bindings(), |mut existing, expr| { + existing.extend(expr.new_bindings()); + existing + }) + } Expression::Block(_, _) => HashSet::new(), Expression::Binding(_, name, expr) => { let mut others = expr.new_bindings(); diff --git a/src/syntax/location.rs b/src/syntax/location.rs index 09e6e0c..bf55823 100644 --- a/src/syntax/location.rs +++ b/src/syntax/location.rs @@ -12,6 +12,10 @@ pub struct Location { location: Range, } +pub trait Located { + fn location(&self) -> &Location; +} + impl Location { /// Generate a new `Location` from a file index and an offset from the /// start of the file. @@ -116,4 +120,36 @@ impl Location { }) } } + + /// Infer a location set by combining all of the information we have + /// in the list of located things. + /// + /// This will attempt to throw away manufactured locations whenever + /// possible, but if there's multiple files mixed in it will likely + /// fail. In all failure cases, including when the set of items is + /// empty, will return a manufactured location to use. + pub fn infer_from(items: &[T]) -> Location { + let mut current = None; + + for item in items { + let location = item.location(); + + if (location.file_idx != 0) + || (location.location.start != 0) + || (location.location.end != 0) + { + current = match current { + None => Some(Some(location.clone())), + Some(None) => Some(None), // we ran into an error somewhere + Some(Some(actual)) => Some(actual.merge(location)), + }; + } + } + + match current { + None => Location::manufactured(), + Some(None) => Location::manufactured(), + Some(Some(x)) => x, + } + } } diff --git a/src/syntax/name.rs b/src/syntax/name.rs index 5ffc5b5..f532de6 100644 --- a/src/syntax/name.rs +++ b/src/syntax/name.rs @@ -2,19 +2,20 @@ use crate::syntax::Location; use internment::ArcIntern; use std::fmt; use std::hash::Hash; +use std::sync::atomic::{AtomicU64, Ordering}; /// The name of a thing in the source language. /// /// In many ways, you can treat this like a string, but it's a very tricky /// string in a couple of ways: -/// +/// /// First, it's a string associated with a particular location in the source /// file, and you can find out what that source location is relatively easily. -/// +/// /// Second, it's a name that retains something of its identity across renaming, /// so that you can keep track of what a variables original name was, as well as /// what it's new name is if it's been renamed. -/// +/// /// Finally, when it comes to equality tests, comparisons, and hashing, `Name` /// uses *only* the new name, if the variable has been renamed, or the original /// name, if it has not been renamed. It never uses the location. This allows @@ -30,7 +31,7 @@ pub struct Name { impl Name { /// Create a new name at the given location. - /// + /// /// This creates an "original" name, which has not been renamed, at the /// given location. pub fn new(n: S, location: Location) -> Name { @@ -42,7 +43,7 @@ impl Name { } /// Create a new name with no location information. - /// + /// /// This creates an "original" name, which has not been renamed, at the /// given location. You should always prefer to use [`Location::new`] if /// there is any possible way to get it, because that will be more @@ -55,8 +56,35 @@ impl Name { } } + /// Create a unique name based on the original name provided. + /// + /// This will automatically append a number and wrap that in + /// <>, which is hoped to be unique. + pub fn gensym(n: S) -> Name { + static GENSYM_COUNTER: AtomicU64 = AtomicU64::new(0); + + let new_name = format!( + "<{}{}>", + n.to_string(), + GENSYM_COUNTER.fetch_add(1, Ordering::SeqCst) + ); + Name { + name: ArcIntern::new(new_name), + rename: None, + location: Location::manufactured(), + } + } + + /// As with gensym, but tie the name to the given location + pub fn located_gensym(location: Location, n: S) -> Name { + Name { + location, + ..Name::gensym(n) + } + } + /// Returns a reference to the original name of the variable. - /// + /// /// Regardless of whether or not the function has been renamed, this will /// return whatever name this variable started with. pub fn original_name(&self) -> &str { @@ -64,11 +92,14 @@ impl Name { } /// Returns a reference to the current name of the variable. - /// + /// /// If the variable has been renamed, it will return that, otherwise we'll /// return the current name. pub fn current_name(&self) -> &str { - self.rename.as_ref().map(|x| x.as_str()).unwrap_or_else(|| self.name.as_str()) + self.rename + .as_ref() + .map(|x| x.as_str()) + .unwrap_or_else(|| self.name.as_str()) } /// Returns the current name of the variable as an interned string. @@ -110,4 +141,3 @@ impl fmt::Display for Name { self.current_name().fmt(f) } } - diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index e2777b5..2a9516a 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -9,7 +9,7 @@ //! eventually want to leave lalrpop behind.) //! use crate::syntax::{Location, ParserError}; -use crate::syntax::ast::{Program,TopLevel,Expression,Value,Type}; +use crate::syntax::ast::{TopLevel,Expression,Value,Type}; use crate::syntax::name::Name; use crate::syntax::tokens::{ConstantType, Token}; use internment::ArcIntern; @@ -63,20 +63,12 @@ extern { } } -pub Program: Program = { - // a program is just a set of statements - => Program { - items - }, - => Program { items: vec![] }, -} - -ProgramTopLevel: Vec = { - => { +pub Program: Vec = { + => { rest.push(t); rest }, - => vec![t], + => vec![], } pub TopLevel: TopLevel = { diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index 3ba412e..e184aaf 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -6,14 +6,78 @@ impl Program { pub fn pretty<'a>(&self, allocator: &'a Allocator<'a>) -> DocBuilder<'a, Allocator<'a>> { let mut result = allocator.nil(); - for tl in self.items.iter() { + for definition in self.structures.values() { result = result - .append(tl.pretty(allocator)) - .append(allocator.text(";")) + .append(allocator.text("struct")) + .append(allocator.space()) + .append(allocator.text(definition.name.original_name().to_string())) + .append(allocator.space()) + .append(allocator.text("{")) + .append(allocator.hardline()) + .append( + allocator + .concat(definition.fields.iter().map(|(name, ty)| { + let mut type_bit = allocator.nil(); + + if let Some(ty) = ty { + type_bit = allocator + .text(":") + .append(allocator.space()) + .append(ty.pretty(allocator)); + } + + allocator + .text(name.original_name().to_string()) + .append(type_bit) + .append(allocator.text(";")) + .append(allocator.hardline()) + })) + .nest(2), + ) + .append(allocator.text("}")) .append(allocator.hardline()); } - result + for definition in self.functions.values() { + let mut return_type_bit = allocator.nil(); + + if let Some(rettype) = definition.return_type.as_ref() { + return_type_bit = allocator + .text("->") + .append(allocator.space()) + .append(rettype.pretty(allocator)); + } + + result = result + .append(allocator.text("function")) + .append(allocator.space()) + .append(allocator.text(definition.name.original_name().to_string())) + .append(allocator.text("(")) + .append(allocator.intersperse( + definition.arguments.iter().map(|(x, t)| { + let mut type_bit = allocator.nil(); + + if let Some(ty) = t { + type_bit = allocator + .text(":") + .append(allocator.space()) + .append(ty.pretty(allocator)); + } + + allocator + .text(x.original_name().to_string()) + .append(type_bit) + }), + allocator.text(","), + )) + .append(allocator.text(")")) + .append(return_type_bit) + .append(allocator.softline()) + .append(definition.body.pretty(allocator)) + .append(allocator.hardline()); + } + + result.append(self.body.pretty(allocator)) } } diff --git a/src/syntax/replace_references.rs b/src/syntax/replace_references.rs new file mode 100644 index 0000000..3c7b7d5 --- /dev/null +++ b/src/syntax/replace_references.rs @@ -0,0 +1,51 @@ +use super::{Expression, Name}; +use std::collections::HashMap; + +impl Expression { + /// Replace all references in the given map to their alternative expression values + pub fn replace_references(&mut self, map: &mut HashMap) { + match self { + Expression::Value(_, _) => {} + Expression::Constructor(_, _, items) => { + for (_, item) in items.iter_mut() { + item.replace_references(map); + } + } + Expression::Reference(name) => match map.get(name) { + None => {} + Some(x) => *self = x.clone(), + }, + Expression::FieldRef(_, subexp, _) => { + subexp.replace_references(map); + } + Expression::Cast(_, _, subexp) => { + subexp.replace_references(map); + } + Expression::Primitive(_, _) => {} + Expression::Call(_, fun, args) => { + fun.replace_references(map); + for arg in args.iter_mut() { + arg.replace_references(map); + } + } + Expression::Block(_, exprs) => { + for expr in exprs.iter_mut() { + expr.replace_references(map); + } + } + Expression::Binding(_, n, expr) => { + expr.replace_references(map); + map.remove(n); + } + Expression::Function(_, mname, args, _, body) => { + if let Some(name) = mname { + map.remove(name); + } + for (arg_name, _) in args.iter() { + map.remove(arg_name); + } + body.replace_references(map); + } + } + } +} diff --git a/src/syntax/validate.rs b/src/syntax/validate.rs index 4db79c8..8655643 100644 --- a/src/syntax/validate.rs +++ b/src/syntax/validate.rs @@ -1,11 +1,13 @@ -use crate::{ - eval::PrimitiveType, - syntax::{Expression, Location, Program, TopLevel}, - util::scoped_map::ScopedMap, -}; +use crate::eval::PrimitiveType; +use crate::syntax::{Expression, Location, Program, StructureDefinition, TopLevel}; +use crate::util::scoped_map::ScopedMap; +use crate::util::warning_result::WarningResult; use codespan_reporting::diagnostic::Diagnostic; +use std::collections::HashMap; use std::str::FromStr; +use super::{FunctionDefinition, Name, Type}; + /// An error we found while validating the input program. /// /// These errors indicate that we should stop trying to compile @@ -66,9 +68,9 @@ impl Program { /// This checks for things like references to variables that don't exist, for /// example, and generates warnings for things that are inadvisable but not /// actually a problem. - pub fn validate(&self) -> (Vec, Vec) { + pub fn validate(raw_syntax: Vec) -> WarningResult { let mut bound_variables = ScopedMap::new(); - self.validate_with_bindings(&mut bound_variables) + Self::validate_with_bindings(raw_syntax, &mut bound_variables) } /// Validate that the program makes semantic sense, not just syntactic sense. @@ -77,118 +79,137 @@ impl Program { /// example, and generates warnings for things that are inadvisable but not /// actually a problem. pub fn validate_with_bindings( - &self, + raw_syntax: Vec, bound_variables: &mut ScopedMap, - ) -> (Vec, Vec) { - let mut errors = vec![]; - let mut warnings = vec![]; + ) -> WarningResult { + let mut functions = HashMap::new(); + let mut structures = HashMap::new(); + let mut result = WarningResult::ok(vec![]); + let location = Location::infer_from(&raw_syntax); - for stmt in self.items.iter() { - let (mut new_errors, mut new_warnings) = stmt.validate_with_bindings(bound_variables); - errors.append(&mut new_errors); - warnings.append(&mut new_warnings); + for stmt in raw_syntax.into_iter() { + match stmt { + TopLevel::Expression(expr) => { + let expr_result = + expr.validate(bound_variables, &mut structures, &mut functions); + result = result.merge_with(expr_result, |mut previous, current| { + previous.push(current); + Ok(previous) + }); + } + + TopLevel::Structure(loc, name, fields) => { + let definition = StructureDefinition::new( + loc, + name.clone(), + fields.into_iter().map(|(n, t)| (n, Some(t))).collect(), + ); + + structures.insert(name, definition); + } + } } - (errors, warnings) - } -} - -impl TopLevel { - /// Validate that the top level item makes semantic sense, not just syntactic - /// sense. - /// - /// This checks for things like references to variables that don't exist, for - /// example, and generates warnings for thins that are inadvisable but not - /// actually a problem. - pub fn validate(&self) -> (Vec, Vec) { - let mut bound_variables = ScopedMap::new(); - self.validate_with_bindings(&mut bound_variables) - } - - /// Validate that the top level item makes semantic sense, not just syntactic - /// sense. - /// - /// This checks for things like references to variables that don't exist, for - /// example, and generates warnings for thins that are inadvisable but not - /// actually a problem. - pub fn validate_with_bindings( - &self, - bound_variables: &mut ScopedMap, - ) -> (Vec, Vec) { - match self { - TopLevel::Expression(expr) => expr.validate(bound_variables), - TopLevel::Structure(_, _, _) => (vec![], vec![]), - } + result.map(move |exprs| Program { + functions, + structures, + body: Expression::Block(location, exprs), + }) } } impl Expression { fn validate( - &self, + self, variable_map: &mut ScopedMap, - ) -> (Vec, Vec) { + structure_map: &mut HashMap, + function_map: &mut HashMap, + ) -> WarningResult { match self { - Expression::Value(_, _) => (vec![], vec![]), - Expression::Constructor(_, _, fields) => { - let mut errors = vec![]; - let mut warnings = vec![]; + Expression::Value(_, _) => WarningResult::ok(self), - for (_, expr) in fields.iter() { - let (mut e, mut w) = expr.validate(variable_map); - errors.append(&mut e); - warnings.append(&mut w); + Expression::Constructor(location, name, fields) => { + let mut result = WarningResult::ok(vec![]); + + for (name, expr) in fields.into_iter() { + let expr_result = expr.validate(variable_map, structure_map, function_map); + result = result.merge_with(expr_result, move |mut fields, new_expr| { + fields.push((name, new_expr)); + Ok(fields) + }); } - (errors, warnings) + result.map(move |fields| Expression::Constructor(location, name, fields)) } - Expression::Reference(var) if variable_map.contains_key(&var.original_name().to_string()) => (vec![], vec![]), - Expression::Reference(var) => ( - vec![Error::UnboundVariable(var.location().clone(), var.original_name().to_string())], - vec![], - ), - Expression::FieldRef(_, exp, _) => exp.validate(variable_map), + + Expression::Reference(ref var) + if variable_map.contains_key(&var.original_name().to_string()) => + { + WarningResult::ok(self) + } + Expression::Reference(var) => WarningResult::err(Error::UnboundVariable( + var.location().clone(), + var.original_name().to_string(), + )), + + Expression::FieldRef(location, exp, field) => exp + .validate(variable_map, structure_map, function_map) + .map(|x| Expression::FieldRef(location, Box::new(x), field)), + Expression::Cast(location, t, expr) => { - let (mut errs, warns) = expr.validate(variable_map); + let mut expr_result = expr.validate(variable_map, structure_map, function_map); - if PrimitiveType::from_str(t).is_err() { - errs.push(Error::UnknownType(location.clone(), t.clone())) + if PrimitiveType::from_str(&t).is_err() { + expr_result.add_error(Error::UnknownType(location.clone(), t.clone())); } - (errs, warns) + expr_result.map(|e| Expression::Cast(location, t, Box::new(e))) } - Expression::Primitive(_, _) => (vec![], vec![]), - Expression::Call(_, func, args) => { - let (mut errors, mut warnings) = func.validate(variable_map); - for arg in args.iter() { - let (mut e, mut w) = arg.validate(variable_map); - errors.append(&mut e); - warnings.append(&mut w); + // FIXME: Check for valid primitives here!! + Expression::Primitive(_, _) => WarningResult::ok(self), + + Expression::Call(loc, func, args) => { + let mut result = func + .validate(variable_map, structure_map, function_map) + .map(|x| (x, vec![])); + + for arg in args.into_iter() { + let expr_result = arg.validate(variable_map, structure_map, function_map); + result = + result.merge_with(expr_result, |(func, mut previous_args), new_arg| { + previous_args.push(new_arg); + Ok((func, previous_args)) + }); } - (errors, warnings) + result.map(|(func, args)| Expression::Call(loc, Box::new(func), args)) } - Expression::Block(_, stmts) => { - let mut errors = vec![]; - let mut warnings = vec![]; - for stmt in stmts.iter() { - let (mut local_errors, mut local_warnings) = stmt.validate(variable_map); + Expression::Block(loc, stmts) => { + let mut result = WarningResult::ok(vec![]); - errors.append(&mut local_errors); - warnings.append(&mut local_warnings); + for stmt in stmts.into_iter() { + let stmt_result = stmt.validate(variable_map, structure_map, function_map); + result = result.merge_with(stmt_result, |mut stmts, stmt| { + stmts.push(stmt); + Ok(stmts) + }); } - (errors, warnings) + result.map(|stmts| Expression::Block(loc, stmts)) } + Expression::Binding(loc, var, val) => { // we're going to make the decision that a variable is not bound in the right // hand side of its binding, which makes a lot of things easier. So we'll just // immediately check the expression, and go from there. - let (errors, mut warnings) = val.validate(variable_map); + let mut result = val.validate(variable_map, structure_map, function_map); - if let Some(original_binding_site) = variable_map.get(&var.original_name().to_string()) { - warnings.push(Warning::ShadowedVariable( + if let Some(original_binding_site) = + variable_map.get(&var.original_name().to_string()) + { + result.add_warning(Warning::ShadowedVariable( original_binding_site.clone(), loc.clone(), var.to_string(), @@ -197,19 +218,118 @@ impl Expression { variable_map.insert(var.to_string(), loc.clone()); } - (errors, warnings) + result.map(|val| Expression::Binding(loc, var, Box::new(val))) } - Expression::Function(_, name, arguments, _, body) => { - if let Some(name) = name { + + Expression::Function(loc, name, mut arguments, return_type, body) => { + let mut result = WarningResult::ok(()); + + // first we should check for shadowing + for new_name in name.iter().chain(arguments.iter().map(|x| &x.0)) { + if let Some(original_site) = variable_map.get(new_name.original_name()) { + result.add_warning(Warning::ShadowedVariable( + original_site.clone(), + loc.clone(), + new_name.original_name().to_string(), + )); + } + } + + // the function name is now available in our current scope, if the function was given one + if let Some(name) = &name { variable_map.insert(name.original_name().to_string(), name.location().clone()); } + + // the arguments are available in a new scope, which we will use to validate the function + // body variable_map.new_scope(); for (arg, _) in arguments.iter() { variable_map.insert(arg.original_name().to_string(), arg.location().clone()); } - let result = body.validate(variable_map); + + let body_result = body.validate(variable_map, structure_map, function_map); variable_map.release_scope(); - result + + body_result.merge_with(result, move |mut body, _| { + // figure out what, if anything, needs to be in the closure for this function. + let mut free_variables = body.free_variables(); + for (n, _) in arguments.iter() { + free_variables.remove(n); + } + // generate a new name for the closure type we're about to create + let closure_type_name = Name::located_gensym( + loc.clone(), + name.as_ref().map(Name::original_name).unwrap_or("closure_"), + ); + // ... and then create a structure type that has all of the free variables + // in it + let closure_type = StructureDefinition::new( + loc.clone(), + closure_type_name.clone(), + free_variables.iter().map(|x| (x.clone(), None)).collect(), + ); + // this will become the first argument of the function, so name it and add + // it to the argument list. + let closure_arg = Name::gensym("__closure_arg"); + arguments.insert( + 0, + ( + closure_arg.clone(), + Some(Type::Named(closure_type_name.clone())), + ), + ); + // Now make a map from the old free variable names to references into + // our closure argument + let rebinds = free_variables + .into_iter() + .map(|n| { + ( + n.clone(), + Expression::FieldRef( + n.location().clone(), + Box::new(Expression::Reference(closure_arg.clone())), + n, + ), + ) + }) + .collect::>(); + let mut rebind_map = rebinds.iter().cloned().collect(); + // and replace all the references in the function with this map + body.replace_references(&mut rebind_map); + // OK! This function definitely needs a name; if the user didn't give + // it one, we'll do so. + let function_name = + name.unwrap_or_else(|| Name::located_gensym(loc.clone(), "function")); + // And finally, we can make the function definition and insert it into our global + // list along with the new closure type. + let function = FunctionDefinition::new( + function_name.clone(), + arguments.clone(), + return_type.clone(), + body, + ); + + structure_map.insert(closure_type_name.clone(), closure_type); + function_map.insert(function_name.clone(), function); + + // And the result of this function is a call to a primitive that generates + // the closure value in some sort of reasonable way. + Ok(Expression::Call( + Location::manufactured(), + Box::new(Expression::Primitive( + Location::manufactured(), + Name::new("", Location::manufactured()), + )), + vec![ + Expression::Reference(function_name), + Expression::Constructor( + Location::manufactured(), + closure_type_name, + rebinds, + ), + ], + )) + }) } } } @@ -217,16 +337,19 @@ impl Expression { #[test] fn cast_checks_are_reasonable() { - let good_stmt = TopLevel::parse(0, "x = 4u8;").expect("valid test case"); - let (good_errs, good_warns) = good_stmt.validate(); + let mut variable_map = ScopedMap::new(); + let mut structure_map = HashMap::new(); + let mut function_map = HashMap::new(); - assert!(good_errs.is_empty()); - assert!(good_warns.is_empty()); + let good_stmt = Expression::parse(0, "x = 4u8;").expect("valid test case"); + let result_good = good_stmt.validate(&mut variable_map, &mut structure_map, &mut function_map); - let bad_stmt = TopLevel::parse(0, "x = 4u8;").expect("valid test case"); - let (bad_errs, bad_warns) = bad_stmt.validate(); + assert!(result_good.is_ok()); + assert!(result_good.warnings().is_empty()); - assert!(bad_warns.is_empty()); - assert_eq!(bad_errs.len(), 1); - assert!(matches!(bad_errs[0], Error::UnknownType(_, ref x) if x == "apple")); + let bad_stmt = Expression::parse(0, "x = 4u8;").expect("valid test case"); + let result_err = bad_stmt.validate(&mut variable_map, &mut structure_map, &mut function_map); + + assert!(result_err.is_err()); + assert!(result_err.warnings().is_empty()); } diff --git a/src/type_infer.rs b/src/type_infer.rs index 94bb3cb..6a8e5e2 100644 --- a/src/type_infer.rs +++ b/src/type_infer.rs @@ -24,26 +24,15 @@ pub use self::result::TypeInferenceResult; use self::warning::TypeInferenceWarning; use crate::ir::ast as ir; use crate::syntax; -#[cfg(test)] -use crate::syntax::arbitrary::GenerationEnvironment; -use internment::ArcIntern; -#[cfg(test)] -use proptest::prelude::Arbitrary; +use crate::syntax::Name; use std::collections::HashMap; -#[derive(Default)] struct InferenceEngine { constraints: Vec, - type_definitions: HashMap, ir::TypeOrVar>, - variable_types: HashMap, ir::TypeOrVar>, - functions: HashMap< - ArcIntern, - ( - Vec<(ArcIntern, ir::TypeOrVar)>, - ir::Expression, - ), - >, - statements: Vec>, + type_definitions: HashMap, + variable_types: HashMap, + functions: HashMap>, + body: ir::Expression, errors: Vec, warnings: Vec, } @@ -55,8 +44,7 @@ impl syntax::Program { /// You really should have made sure that this program was validated before running /// this method, otherwise you may experience panics during operation. pub fn type_infer(self) -> TypeInferenceResult> { - let mut engine = InferenceEngine::default(); - engine.injest_program(self); + let mut engine = InferenceEngine::from(self); engine.solve_constraints(); if engine.errors.is_empty() { @@ -89,10 +77,11 @@ impl syntax::Program { proptest::proptest! { #[test] - fn translation_maintains_semantics(input in syntax::Program::arbitrary_with(GenerationEnvironment::new(false))) { - let syntax_result = input.eval().map(|(x,o)| (x.strip(), o)); - let ir = input.type_infer().expect("arbitrary should generate type-safe programs"); - let ir_evaluator = crate::ir::Evaluator::default(); + fn translation_maintains_semantics(input in syntax::arbitrary::ProgramGenerator::default()) { + let input_program = syntax::Program::validate(input).into_result().expect("can validate random program"); + let syntax_result = input_program.eval().map(|(x,o)| (x.strip(), o)); + let ir = input_program.type_infer().expect("arbitrary should generate type-safe programs"); + let ir_evaluator = crate::ir::Evaluator::::default(); let ir_result = ir_evaluator.eval(ir).map(|(x, o)| (x.strip(), o)); match (syntax_result, ir_result) { (Err(e1), Err(e2)) => proptest::prop_assert_eq!(e1, e2), diff --git a/src/type_infer/constraint.rs b/src/type_infer/constraint.rs index e6520fb..6d05ba2 100644 --- a/src/type_infer/constraint.rs +++ b/src/type_infer/constraint.rs @@ -1,6 +1,5 @@ use crate::ir::TypeOrVar; -use crate::syntax::Location; -use internment::ArcIntern; +use crate::syntax::{Location, Name}; use std::fmt; /// A type inference constraint that we're going to need to solve. @@ -14,7 +13,7 @@ pub enum Constraint { CanCastTo(Location, TypeOrVar, TypeOrVar), /// The given type has the given field in it, and the type of that field /// is as given. - TypeHasField(Location, TypeOrVar, ArcIntern, TypeOrVar), + TypeHasField(Location, TypeOrVar, Name, TypeOrVar), /// The given type must be some numeric type, but this is not a constant /// value, so don't try to default it if we can't figure it out NumericType(Location, TypeOrVar), @@ -29,7 +28,7 @@ pub enum Constraint { /// The given type can be negated IsSigned(Location, TypeOrVar), /// Checks to see if the given named type is equivalent to the provided one. - NamedTypeIs(Location, ArcIntern, TypeOrVar), + NamedTypeIs(Location, Name, TypeOrVar), } impl fmt::Display for Constraint { @@ -56,7 +55,7 @@ impl Constraint { /// with the given type. /// /// Returns whether or not anything was changed in the constraint. - pub fn replace(&mut self, name: &ArcIntern, replace_with: &TypeOrVar) -> bool { + pub fn replace(&mut self, name: &Name, replace_with: &TypeOrVar) -> bool { match self { Constraint::Printable(_, ty) => ty.replace(name, replace_with), Constraint::FitsInNumType(_, ty, _) => ty.replace(name, replace_with), diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 2eac333..1c0060d 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -1,74 +1,109 @@ use super::constraint::Constraint; use super::InferenceEngine; use crate::eval::PrimitiveType; -use crate::ir; -use crate::syntax::{self, ConstantType}; +use crate::ir::{self, Fields}; +use crate::syntax::Name; +use crate::syntax::{self, ConstantType, Location}; use crate::util::scoped_map::ScopedMap; use internment::ArcIntern; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::str::FromStr; -struct ExpressionInfo { - expression: ir::Expression, - result_type: ir::TypeOrVar, - free_variables: HashSet>, - bound_variables: HashSet>, -} +impl From for InferenceEngine { + fn from(value: syntax::Program) -> Self { + let syntax::Program { + functions, + structures, + body, + } = value; + let mut result = InferenceEngine { + constraints: Vec::new(), + type_definitions: HashMap::new(), + variable_types: HashMap::new(), + functions: HashMap::new(), + body: ir::Expression::Block(Location::manufactured(), ir::TypeOrVar::new(), vec![]), + errors: vec![], + warnings: vec![], + }; + let mut renames = ScopedMap::new(); -impl ExpressionInfo { - fn simple(expression: ir::Expression, result_type: ir::TypeOrVar) -> Self { - ExpressionInfo { - expression, - result_type, - free_variables: HashSet::new(), - bound_variables: HashSet::new(), + // first let's transfer all the type information over into our new + // data structures + for (_, structure) in structures.into_iter() { + let mut fields = Fields::default(); + + for (name, optty) in structure.fields { + match optty { + None => { + let newty = ir::TypeOrVar::new_located(name.location().clone()); + fields.insert(name, newty); + } + + Some(t) => { + let existing_ty = result.convert_type(t); + fields.insert(name, existing_ty); + } + } + } + + result + .type_definitions + .insert(structure.name.clone(), ir::TypeOrVar::Structure(fields)); } + + // then transfer all the functions over to the new system + for (_, function) in functions.into_iter() { + // convert the arguments into the new type scheme. if given, use the ones + // given, otherwise generate a new type variable for us to solve for. + let mut arguments = vec![]; + for (name, ty) in function.arguments.into_iter() { + match ty { + None => { + let inferred_type = ir::TypeOrVar::new_located(name.location().clone()); + arguments.push((name, inferred_type)); + } + + Some(t) => { + arguments.push((name, result.convert_type(t))); + } + } + } + + // similarly, use the provided return type if given, otherwise generate + // a new type variable to use. + let return_type = if let Some(t) = function.return_type { + result.convert_type(t) + } else { + ir::TypeOrVar::new_located(function.name.location().clone()) + }; + + let (body, body_type) = result.convert_expression(function.body, &mut renames); + result.constraints.push(Constraint::Equivalent( + function.name.location().clone(), + return_type.clone(), + body_type, + )); + + let new_function = ir::FunctionDefinition { + name: function.name, + arguments, + return_type, + body, + }; + + result + .functions + .insert(new_function.name.clone(), new_function); + } + + // finally we can transfer the body over + result.body = result.convert_expression(body, &mut renames).0; + + result } } impl InferenceEngine { - /// This function takes a syntactic program and converts it into the IR version of the - /// program, with appropriate type variables introduced and their constraints added to - /// the given database. - /// - /// If the input function has been validated (which it should be), then this should run - /// into no error conditions. However, if you failed to validate the input, then this - /// function can panic. - pub fn injest_program(&mut self, program: syntax::Program) { - let mut renames = ScopedMap::new(); - - for item in program.items.into_iter() { - self.convert_top_level(item, &mut renames); - } - } - - /// This function takes a top-level item and converts it into the IR version of the - /// program, with all the appropriate type variables introduced and their constraints - /// added to the given database. - fn convert_top_level( - &mut self, - top_level: syntax::TopLevel, - renames: &mut ScopedMap, ArcIntern>, - ) { - match top_level { - syntax::TopLevel::Expression(expr) => { - let expr_info = self.convert_expression(expr, renames); - self.statements.push(expr_info.expression); - } - - syntax::TopLevel::Structure(_loc, name, fields) => { - let mut updated_fields = ir::Fields::default(); - - for (name, field_type) in fields.into_iter() { - updated_fields.insert(name.intern(), self.convert_type(field_type)); - } - - self.type_definitions - .insert(name.intern(), ir::TypeOrVar::Structure(updated_fields)); - } - } - } - /// This function takes a syntactic expression and converts it into a series /// of IR statements, adding type variables and constraints as necessary. /// @@ -83,8 +118,8 @@ impl InferenceEngine { fn convert_expression( &mut self, expression: syntax::Expression, - renames: &mut ScopedMap, ArcIntern>, - ) -> ExpressionInfo { + renames: &mut ScopedMap>, + ) -> (ir::Expression, ir::TypeOrVar) { match expression { // converting values is mostly tedious, because there's so many cases // involved @@ -145,7 +180,7 @@ impl InferenceEngine { value, )); - ExpressionInfo::simple( + ( ir::Expression::Atomic(ir::ValueOrRef::Value(loc, newtype.clone(), newval)), newtype, ) @@ -156,93 +191,73 @@ impl InferenceEngine { let mut result_fields = HashMap::new(); let mut type_fields = ir::Fields::default(); let mut prereqs = vec![]; - let mut free_variables = HashSet::new(); - let mut bound_variables = HashSet::new(); for (name, syntax_expr) in fields.into_iter() { - let field_expr_info = self.convert_expression(syntax_expr, renames); - type_fields.insert(name.clone().intern(), field_expr_info.result_type); - let (prereq, value) = simplify_expr(field_expr_info.expression); - result_fields.insert(name.clone().intern(), value); + let (field_expr, field_type) = self.convert_expression(syntax_expr, renames); + type_fields.insert(name.clone(), field_type); + let (prereq, value) = simplify_expr(field_expr); + result_fields.insert(name.clone(), value); merge_prereq(&mut prereqs, prereq); - free_variables.extend(field_expr_info.free_variables); - bound_variables.extend(field_expr_info.bound_variables); } let result_type = ir::TypeOrVar::Structure(type_fields); self.constraints.push(Constraint::NamedTypeIs( loc.clone(), - name.clone().intern(), + name.clone(), result_type.clone(), )); - let expression = ir::Expression::Construct( - loc, - result_type.clone(), - name.intern(), - result_fields, - ); + let expression = + ir::Expression::Construct(loc, result_type.clone(), name, result_fields); - ExpressionInfo { - expression, - result_type, - free_variables, - bound_variables, - } + (expression, result_type) } syntax::Expression::Reference(mut name) => { - if let Some(rename) = renames.get(name.current_interned()) { + if let Some(rename) = renames.get(&name) { name.rename(rename); } + let result_type = self .variable_types - .get(name.current_interned()) + .get(&name) .cloned() .expect("variable bound before use"); + let expression = ir::Expression::Atomic(ir::ValueOrRef::Ref( name.location().clone(), result_type.clone(), - name.current_interned().clone(), + name.clone(), )); - let free_variables = HashSet::from([name.current_interned().clone()]); - ExpressionInfo { - expression, - result_type, - free_variables, - bound_variables: HashSet::new(), - } + (expression, result_type) } syntax::Expression::FieldRef(loc, expr, field) => { - let mut expr_info = self.convert_expression(*expr, renames); - let (prereqs, val_or_ref) = simplify_expr(expr_info.expression); + let (expr, expr_type) = self.convert_expression(*expr, renames); + let (prereqs, val_or_ref) = simplify_expr(expr); let result_type = ir::TypeOrVar::new(); let result = ir::Expression::FieldRef( loc.clone(), result_type.clone(), - expr_info.result_type.clone(), + expr_type.clone(), val_or_ref, - field.clone().intern(), + field.clone(), ); self.constraints.push(Constraint::TypeHasField( loc, - expr_info.result_type.clone(), - field.intern(), + expr_type.clone(), + field, result_type.clone(), )); - expr_info.expression = finalize_expression(prereqs, result); - expr_info.result_type = result_type; - - expr_info + (finalize_expression(prereqs, result), result_type) } syntax::Expression::Cast(loc, target, expr) => { - let mut expr_info = self.convert_expression(*expr, renames); - let (prereqs, val_or_ref) = simplify_expr(expr_info.expression); + let (expr, expr_type) = self.convert_expression(*expr, renames); + let (prereqs, val_or_ref) = simplify_expr(expr); let target_type: ir::TypeOrVar = PrimitiveType::from_str(&target) .expect("valid type for cast") .into(); @@ -250,14 +265,11 @@ impl InferenceEngine { self.constraints.push(Constraint::CanCastTo( loc, - expr_info.result_type.clone(), + expr_type.clone(), target_type.clone(), )); - expr_info.expression = finalize_expression(prereqs, res); - expr_info.result_type = target_type; - - expr_info + (finalize_expression(prereqs, res), target_type) } syntax::Expression::Primitive(loc, name) => { @@ -273,7 +285,7 @@ impl InferenceEngine { Box::new(numeric_type.clone()), ); let result_value = ir::ValueOrRef::Primitive(loc, funtype.clone(), primop); - ExpressionInfo::simple(ir::Expression::Atomic(result_value), funtype) + (ir::Expression::Atomic(result_value), funtype) } ir::Primitive::Minus => { @@ -285,7 +297,7 @@ impl InferenceEngine { Box::new(numeric_type.clone()), ); let result_value = ir::ValueOrRef::Primitive(loc, funtype.clone(), primop); - ExpressionInfo::simple(ir::Expression::Atomic(result_value), funtype) + (ir::Expression::Atomic(result_value), funtype) } ir::Primitive::Print => { @@ -297,7 +309,7 @@ impl InferenceEngine { Box::new(ir::TypeOrVar::Primitive(PrimitiveType::Void)), ); let result_value = ir::ValueOrRef::Primitive(loc, funtype.clone(), primop); - ExpressionInfo::simple(ir::Expression::Atomic(result_value), funtype) + (ir::Expression::Atomic(result_value), funtype) } ir::Primitive::Negate => { @@ -309,7 +321,7 @@ impl InferenceEngine { let funtype = ir::TypeOrVar::Function(vec![arg_type.clone()], Box::new(arg_type)); let result_value = ir::ValueOrRef::Primitive(loc, funtype.clone(), primop); - ExpressionInfo::simple(ir::Expression::Atomic(result_value), funtype) + (ir::Expression::Atomic(result_value), funtype) } } } @@ -321,34 +333,32 @@ impl InferenceEngine { .map(|_| ir::TypeOrVar::new()) .collect::>(); - let mut expr_info = self.convert_expression(*fun, renames); + let (fun, fun_type) = self.convert_expression(*fun, renames); let target_fun_type = ir::TypeOrVar::Function(arg_types.clone(), Box::new(return_type.clone())); self.constraints.push(Constraint::Equivalent( loc.clone(), - expr_info.result_type, + fun_type, target_fun_type, )); let mut prereqs = vec![]; - let (fun_prereqs, fun) = simplify_expr(expr_info.expression); + let (fun_prereqs, fun) = simplify_expr(fun); merge_prereq(&mut prereqs, fun_prereqs); let new_args = args .into_iter() .zip(arg_types) .map(|(arg, target_type)| { - let arg_info = self.convert_expression(arg, renames); - let location = arg_info.expression.location().clone(); - let (arg_prereq, new_valref) = simplify_expr(arg_info.expression); + let (arg, arg_type) = self.convert_expression(arg, renames); + let location = arg.location().clone(); + let (arg_prereq, new_valref) = simplify_expr(arg); merge_prereq(&mut prereqs, arg_prereq); self.constraints.push(Constraint::Equivalent( location, - arg_info.result_type, + arg_type, target_type, )); - expr_info.free_variables.extend(arg_info.free_variables); - expr_info.bound_variables.extend(arg_info.bound_variables); new_valref }) .collect(); @@ -356,140 +366,40 @@ impl InferenceEngine { let last_call = ir::Expression::Call(loc.clone(), return_type.clone(), fun, new_args); - expr_info.expression = finalize_expressions(prereqs, last_call); - expr_info.result_type = return_type; - - expr_info + (finalize_expressions(prereqs, last_call), return_type) } syntax::Expression::Block(loc, stmts) => { let mut result_type = ir::TypeOrVar::Primitive(PrimitiveType::Void); let mut exprs = vec![]; - let mut free_variables = HashSet::new(); - let mut bound_variables = HashSet::new(); for xpr in stmts.into_iter() { - let expr_info = self.convert_expression(xpr, renames); - result_type = expr_info.result_type; - exprs.push(expr_info.expression); - free_variables.extend( - expr_info - .free_variables - .difference(&bound_variables) - .cloned() - .collect::>(), - ); - bound_variables.extend(expr_info.bound_variables); + let (expr, expr_type) = self.convert_expression(xpr, renames); + result_type = expr_type; + exprs.push(expr); } - ExpressionInfo { - expression: ir::Expression::Block(loc, result_type.clone(), exprs), + ( + ir::Expression::Block(loc, result_type.clone(), exprs), result_type, - free_variables, - bound_variables, - } + ) } syntax::Expression::Binding(loc, name, expr) => { - let mut expr_info = self.convert_expression(*expr, renames); + let (expr, expr_type) = self.convert_expression(*expr, renames); let final_name = self.finalize_name(renames, name); self.variable_types - .insert(final_name.clone(), expr_info.result_type.clone()); - expr_info.expression = ir::Expression::Bind( - loc, - final_name.clone(), - expr_info.result_type.clone(), - Box::new(expr_info.expression), - ); - expr_info.bound_variables.insert(final_name); - expr_info + .insert(final_name.clone(), expr_type.clone()); + let result_expr = + ir::Expression::Bind(loc, final_name, expr_type.clone(), Box::new(expr)); + + (result_expr, expr_type) } - syntax::Expression::Function(loc, name, args, _, expr) => { - // First, at some point we're going to want to know a location for this function, - // which should either be the name if we have one, or the body if we don't. - let function_location = match name { - None => loc, - Some(ref name) => name.location().clone(), - }; - // Next, let us figure out what we're going to name this function. If the user - // didn't provide one, we'll just call it "function:" for them. (We'll - // want a name for this function, eventually, so we might as well do it now.) - // - // If they did provide a name, see if we're shadowed. IF we are, then we'll have - // to specialize the name a bit. Otherwise we'll stick with their name. - let function_name = match name { - None => ir::gensym("function"), - Some(unbound) => self.finalize_name(renames, unbound), - }; - - // This function is going to have a type. We don't know what it is, but it'll have - // one. - let function_type = ir::TypeOrVar::new(); - self.variable_types - .insert(function_name.clone(), function_type.clone()); - - // Then, let's figure out what to do with the argument names, which similarly - // may need to be renamed. We'll also generate some new type variables to associate - // with all of them. - // - // Note that we want to do all this in a new renaming scope, so that we shadow - // appropriately. - renames.new_scope(); - let arginfo = args - .into_iter() - .map(|(name, mut declared_type)| { - let new_type = ir::TypeOrVar::new(); - self.constraints.push(Constraint::IsSomething( - name.location().clone(), - new_type.clone(), - )); - let new_name = self.finalize_name(renames, name.clone()); - self.variable_types - .insert(new_name.clone(), new_type.clone()); - - if let Some(declared_type) = declared_type.take() { - let declared_type = self.convert_type(declared_type); - self.constraints.push(Constraint::Equivalent( - name.location().clone(), - new_type.clone(), - declared_type, - )); - } - - (new_name, new_type) - }) - .collect::>(); - - // Now we manufacture types for the outputs and then a type for the function itself. - // We're not going to make any claims on these types, yet; they're all just unknown - // type variables we need to work out. - let rettype = ir::TypeOrVar::new(); - let actual_function_type = ir::TypeOrVar::Function( - arginfo.iter().map(|x| x.1.clone()).collect(), - Box::new(rettype.clone()), + syntax::Expression::Function(_, _, _, _, _) => { + panic!( + "Function expressions should not survive validation to get to type checking!" ); - self.constraints.push(Constraint::Equivalent( - function_location, - function_type, - actual_function_type, - )); - - // Now let's convert the body over to the new IR. - let expr_info = self.convert_expression(*expr, renames); - self.constraints.push(Constraint::Equivalent( - expr_info.expression.location().clone(), - rettype.clone(), - expr_info.result_type.clone(), - )); - - // Remember to exit this scoping level! - renames.release_scope(); - - self.functions - .insert(function_name, (arginfo, expr_info.expression.clone())); - - unimplemented!() } } } @@ -501,13 +411,14 @@ impl InferenceEngine { let retval = ir::TypeOrVar::new_located(x.location().clone()); self.constraints.push(Constraint::NamedTypeIs( x.location().clone(), - x.intern(), + x, retval.clone(), )); retval } Ok(v) => ir::TypeOrVar::Primitive(v), }, + syntax::Type::Struct(fields) => { let mut new_fields = ir::Fields::default(); @@ -515,7 +426,7 @@ impl InferenceEngine { let new_field_type = field_type .map(|x| self.convert_type(x)) .unwrap_or_else(ir::TypeOrVar::new); - new_fields.insert(name.intern(), new_field_type); + new_fields.insert(name, new_field_type); } ir::TypeOrVar::Structure(new_fields) @@ -525,18 +436,16 @@ impl InferenceEngine { fn finalize_name( &mut self, - renames: &mut ScopedMap, ArcIntern>, - name: syntax::Name, - ) -> ArcIntern { - if self - .variable_types - .contains_key(name.current_interned()) - { - let new_name = ir::gensym(name.original_name()); - renames.insert(name.current_interned().clone(), new_name.clone()); - new_name + renames: &mut ScopedMap>, + mut name: syntax::Name, + ) -> syntax::Name { + if self.variable_types.contains_key(&name) { + let new_name = Name::gensym(name.original_name()).intern(); + renames.insert(name.clone(), new_name.clone()); + name.rename(&new_name); + name } else { - ArcIntern::new(name.to_string()) + name } } } @@ -552,7 +461,7 @@ fn simplify_expr( expr => { let etype = expr.type_of().clone(); let loc = expr.location().clone(); - let nname = ir::gensym("g"); + let nname = Name::located_gensym(loc.clone(), "g"); let nbinding = ir::Expression::Bind(loc.clone(), nname.clone(), etype.clone(), Box::new(expr)); diff --git a/src/type_infer/error.rs b/src/type_infer/error.rs index dcd0a6c..c611be5 100644 --- a/src/type_infer/error.rs +++ b/src/type_infer/error.rs @@ -1,9 +1,8 @@ use super::constraint::Constraint; use crate::eval::PrimitiveType; use crate::ir::{Primitive, TypeOrVar}; -use crate::syntax::Location; +use crate::syntax::{Location, Name}; use codespan_reporting::diagnostic::Diagnostic; -use internment::ArcIntern; /// The various kinds of errors that can occur while doing type inference. pub enum TypeInferenceError { @@ -30,9 +29,9 @@ pub enum TypeInferenceError { /// The given type isn't signed, and can't be negated IsNotSigned(Location, TypeOrVar), /// The given type doesn't have the given field. - NoFieldForType(Location, ArcIntern, TypeOrVar), + NoFieldForType(Location, Name, TypeOrVar), /// There is no type with the given name. - UnknownTypeName(Location, ArcIntern), + UnknownTypeName(Location, Name), } impl From for Diagnostic { diff --git a/src/type_infer/finalize.rs b/src/type_infer/finalize.rs index 9265953..9ab53fe 100644 --- a/src/type_infer/finalize.rs +++ b/src/type_infer/finalize.rs @@ -1,60 +1,57 @@ use crate::eval::PrimitiveType; -use crate::ir::{Expression, Program, TopLevel, Type, TypeOrVar, TypeWithVoid, Value, ValueOrRef}; -use crate::syntax::Location; -use internment::ArcIntern; +use crate::ir::{Expression, FunctionDefinition, Program, Type, TypeOrVar, Value, ValueOrRef}; +use crate::syntax::Name; use std::collections::HashMap; -pub type TypeResolutions = HashMap, Type>; +pub type TypeResolutions = HashMap; impl super::InferenceEngine { pub fn finalize_program(self, resolutions: TypeResolutions) -> Program { + // we can't do this in place without some type nonsense, so we're going to + // create a brand new set of program arguments and then construct the new + // `Program` from them. + let mut functions = HashMap::new(); + let mut type_definitions = HashMap::new(); + + // this is handy for debugging for (name, ty) in resolutions.iter() { tracing::debug!(name = %name, resolved_type = %ty, "resolved type variable"); } - let mut type_definitions = HashMap::new(); - let mut items = Vec::new(); - + // copy over the type definitions for (name, def) in self.type_definitions.into_iter() { type_definitions.insert(name, finalize_type(def, &resolutions)); } - for (name, (arguments, body)) in self.functions.into_iter() { - let new_body = finalize_expression(body, &resolutions); - let arguments = arguments + // now copy over the functions + for (name, function_def) in self.functions.into_iter() { + assert_eq!(name, function_def.name); + + let body = finalize_expression(function_def.body, &resolutions); + let arguments = function_def + .arguments .into_iter() .map(|(name, t)| (name, finalize_type(t, &resolutions))) .collect(); - items.push(TopLevel::Function( + + functions.insert( name, - arguments, - new_body.type_of(), - new_body, - )); + FunctionDefinition { + name: function_def.name, + arguments, + return_type: body.type_of(), + body, + }, + ); } - let mut body = vec![]; - let mut last_type = Type::void(); - let mut location = None; - - for expr in self.statements.into_iter() { - let next = finalize_expression(expr, &resolutions); - location = location - .map(|x: Location| x.merge(next.location())) - .unwrap_or_else(|| Some(next.location().clone())); - last_type = next.type_of(); - body.push(next); - } - - items.push(TopLevel::Statement(Expression::Block( - location.unwrap_or_else(Location::manufactured), - last_type, - body, - ))); + // and now we can finally compute the new body + let body = finalize_expression(self.body, &resolutions); Program { - items, + functions, type_definitions, + body, } } } diff --git a/src/type_infer/solve.rs b/src/type_infer/solve.rs index 74f1c9f..e2a162e 100644 --- a/src/type_infer/solve.rs +++ b/src/type_infer/solve.rs @@ -3,7 +3,7 @@ use super::error::TypeInferenceError; use super::warning::TypeInferenceWarning; use crate::eval::PrimitiveType; use crate::ir::TypeOrVar; -use internment::ArcIntern; +use crate::syntax::Name; impl super::InferenceEngine { /// Solve all the constraints in the provided database. @@ -562,7 +562,7 @@ impl super::InferenceEngine { /// runs if you don't want to lose it. (If you do want to lose it, of course, go ahead.) fn replace_variable( constraint_db: &mut Vec, - variable: &ArcIntern, + variable: &Name, replace_with: &TypeOrVar, ) -> bool { let mut changed_anything = false; diff --git a/src/util.rs b/src/util.rs index 5e15869..d872744 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,2 +1,3 @@ pub mod pretty; pub mod scoped_map; +pub mod warning_result; diff --git a/src/util/warning_result.rs b/src/util/warning_result.rs new file mode 100644 index 0000000..ff410a8 --- /dev/null +++ b/src/util/warning_result.rs @@ -0,0 +1,155 @@ +use codespan_reporting::diagnostic::Diagnostic; + +/// This type is like `Result`, except that both the Ok case and Err case +/// can also include warning data. +/// +/// Unfortunately, this type cannot be used with `?`, and so should probably +/// only be used when it's really, really handy to be able to carry this +/// sort of information. But when it is handy (for example, in type checking +/// and in early validation), it's really useful. +pub enum WarningResult { + Ok(T, Vec), + Err(Vec, Vec), +} + +impl WarningResult { + pub fn ok(value: T) -> Self { + WarningResult::Ok(value, vec![]) + } + + pub fn err(error: E) -> Self { + WarningResult::Err(vec![error], vec![]) + } + + pub fn is_ok(&self) -> bool { + matches!(self, WarningResult::Ok(_, _)) + } + + pub fn is_err(&self) -> bool { + matches!(self, WarningResult::Err(_, _)) + } + + pub fn warnings(&self) -> &[W] { + match self { + WarningResult::Ok(_, warns) => warns.as_slice(), + WarningResult::Err(_, warns) => warns.as_slice(), + } + } + + pub fn into_result(self) -> Option { + match self { + WarningResult::Ok(v, _) => Some(v), + WarningResult::Err(_, _) => None, + } + } + + pub fn into_errors(self) -> Option> { + match self { + WarningResult::Ok(_, _) => None, + WarningResult::Err(errs, _) => Some(errs), + } + } + + pub fn add_warning(&mut self, warning: W) { + match self { + WarningResult::Ok(_, warns) => warns.push(warning), + WarningResult::Err(_, warns) => warns.push(warning), + } + } + + pub fn add_error(&mut self, error: E) { + match self { + WarningResult::Ok(_, warns) => { + *self = WarningResult::Err(vec![error], std::mem::take(warns)) + } + WarningResult::Err(errs, _) => errs.push(error), + } + } + + pub fn modify(&mut self, f: F) + where + F: FnOnce(&mut T), + { + if let WarningResult::Ok(v, _) = self { + f(v); + } + } + + pub fn map(self, f: F) -> WarningResult + where + F: FnOnce(T) -> R, + { + match self { + WarningResult::Ok(v, ws) => WarningResult::Ok(f(v), ws), + WarningResult::Err(e, ws) => WarningResult::Err(e, ws), + } + } + + /// Merges two results together using the given function to combine `Ok` + /// results into a single value. + /// + pub fn merge_with(mut self, other: WarningResult, f: F) -> WarningResult + where + F: FnOnce(T, O) -> Result, + { + match self { + WarningResult::Err(ref mut errors1, ref mut warns1) => match other { + WarningResult::Err(mut errors2, mut warns2) => { + errors1.append(&mut errors2); + warns1.append(&mut warns2); + self + } + + WarningResult::Ok(_, mut ws) => { + warns1.append(&mut ws); + self + } + }, + + WarningResult::Ok(value1, mut warns1) => match other { + WarningResult::Err(errors, mut warns2) => { + warns2.append(&mut warns1); + WarningResult::Err(errors, warns2) + } + + WarningResult::Ok(value2, mut warns2) => { + warns1.append(&mut warns2); + match f(value1, value2) { + Ok(final_value) => WarningResult::Ok(final_value, warns1), + Err(e) => WarningResult::Err(vec![e], warns1), + } + } + }, + } + } +} + +impl WarningResult +where + W: Into>, + E: Into>, +{ + /// Returns the complete set of diagnostics (warnings and errors) as an + /// Iterator. + /// + /// This function removes the diagnostics from the result! So calling + /// this twice is not advised. + pub fn diagnostics(&mut self) -> impl Iterator> { + match self { + WarningResult::Err(errors, warnings) => std::mem::take(errors) + .into_iter() + .map(Into::into) + .chain(std::mem::take(warnings).into_iter().map(Into::into)), + WarningResult::Ok(_, warnings) => + // this is a moderately ridiculous hack to get around + // the two match arms returning different iterator + // types + { + vec![] + .into_iter() + .map(Into::into) + .chain(std::mem::take(warnings).into_iter().map(Into::into)) + } + } + } +} -- 2.53.0 From cf7eff7a93f3f7001a5869cc95fb8effb8d62506 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Mon, 3 Jun 2024 20:38:45 -0700 Subject: [PATCH 56/59] Remove IR TopLevel. --- src/ir/arbitrary.rs | 62 ++++++++++++++++++++++----------------------- src/ir/ast.rs | 28 -------------------- src/ir/pretty.rs | 30 +--------------------- 3 files changed, 31 insertions(+), 89 deletions(-) diff --git a/src/ir/arbitrary.rs b/src/ir/arbitrary.rs index ce1aa24..16f8b99 100644 --- a/src/ir/arbitrary.rs +++ b/src/ir/arbitrary.rs @@ -1,7 +1,5 @@ use crate::eval::PrimitiveType; -use crate::ir::{ - Expression, Name, Primitive, Program, TopLevel, Type, TypeWithVoid, Value, ValueOrRef, -}; +use crate::ir::{Expression, Name, Primitive, Program, Type, TypeWithVoid, Value, ValueOrRef}; use crate::syntax::Location; use crate::util::scoped_map::ScopedMap; use proptest::strategy::{NewTree, Strategy, ValueTree}; @@ -183,37 +181,37 @@ pub struct ProgramTree { impl ProgramTree { fn new(mut rng: TestRng) -> Self { - let mut items = vec![]; + // let mut items = vec![]; let program_length = PROGRAM_LENGTH_DISTRIBUTION.sample(&mut rng); - let mut env = ScopedMap::new(); + // let mut env = ScopedMap::new(); - for _ in 0..program_length { - match STATEMENT_TYPE_FREQUENCIES[STATEMENT_TYPE_DISTRIBUTION.sample(&mut rng)].0 { - StatementType::Binding => { - let binding = generate_random_binding(&mut rng, &mut env); - items.push(TopLevel::Statement(binding)); - } - StatementType::Expression => { - let expr = generate_random_expression(&mut rng, &mut env); - items.push(TopLevel::Statement(expr)); - } - StatementType::Function => { - env.new_scope(); - let name = generate_random_name(&mut rng); - let mut args = vec![]; - let arg_count = FUNCTION_ARGUMENTS_DISTRIBUTION.sample(&mut rng); - for _ in 0..arg_count { - let name = generate_random_name(&mut rng); - let ty = generate_random_argument_type(&mut rng); - args.push((name, ty)); - } - let body = generate_random_expression(&mut rng, &mut env); - let rettype = body.type_of(); - env.release_scope(); - items.push(TopLevel::Function(name, args, rettype, body)) - } - } - } + // for _ in 0..program_length { + // match STATEMENT_TYPE_FREQUENCIES[STATEMENT_TYPE_DISTRIBUTION.sample(&mut rng)].0 { + // StatementType::Binding => { + // let binding = generate_random_binding(&mut rng, &mut env); + // items.push(TopLevel::Statement(binding)); + // } + // StatementType::Expression => { + // let expr = generate_random_expression(&mut rng, &mut env); + // items.push(TopLevel::Statement(expr)); + // } + // StatementType::Function => { + // env.new_scope(); + // let name = generate_random_name(&mut rng); + // let mut args = vec![]; + // let arg_count = FUNCTION_ARGUMENTS_DISTRIBUTION.sample(&mut rng); + // for _ in 0..arg_count { + // let name = generate_random_name(&mut rng); + // let ty = generate_random_argument_type(&mut rng); + // args.push((name, ty)); + // } + // let body = generate_random_expression(&mut rng, &mut env); + // let rettype = body.type_of(); + // env.release_scope(); + // items.push(TopLevel::Function(name, args, rettype, body)) + // } + // } + // } let current = Program { functions: HashMap::new(), diff --git a/src/ir/ast.rs b/src/ir/ast.rs index 8920d69..d775a91 100644 --- a/src/ir/ast.rs +++ b/src/ir/ast.rs @@ -52,32 +52,6 @@ impl Arbitrary for Program { } } -/// A thing that can sit at the top level of a file. -/// -/// For the moment, these are statements and functions. Other things -/// will likely be added in the future, but for now: just statements -/// and functions -#[derive(Clone, Debug)] -pub enum TopLevel { - Statement(Expression), - // FIXME: Is the return type actually necessary, given we can infer it from - // the expression type? - Function(Name, Vec<(Name, Type)>, Type, Expression), -} - -impl TopLevel { - /// Return the type of the item, as inferred or recently - /// computed. - pub fn type_of(&self) -> T { - match self { - TopLevel::Statement(expr) => expr.type_of(), - TopLevel::Function(_, args, ret, _) => { - T::build_function_type(args.iter().map(|(_, t)| t.clone()).collect(), ret.clone()) - } - } - } -} - /// The representation of an expression. /// /// Note that expressions, like everything else in this syntax tree, @@ -479,8 +453,6 @@ fn struct_sizes_are_rational() { assert_eq!(80, std::mem::size_of::>()); assert_eq!(200, std::mem::size_of::>()); assert_eq!(200, std::mem::size_of::>()); - assert_eq!(272, std::mem::size_of::>()); - assert_eq!(272, std::mem::size_of::>()); assert_eq!(72, std::mem::size_of::>()); assert_eq!(72, std::mem::size_of::>()); } diff --git a/src/ir/pretty.rs b/src/ir/pretty.rs index 9c20e74..4b80178 100644 --- a/src/ir/pretty.rs +++ b/src/ir/pretty.rs @@ -1,4 +1,4 @@ -use crate::ir::{Expression, Primitive, Program, TopLevel, Type, TypeOrVar, Value, ValueOrRef}; +use crate::ir::{Expression, Primitive, Program, Type, TypeOrVar, Value, ValueOrRef}; use crate::syntax::{self, ConstantType}; use crate::util::pretty::{derived_display, pretty_function_type, Allocator}; use pretty::{Arena, DocAllocator, DocBuilder}; @@ -57,33 +57,6 @@ impl Program { } } -impl TopLevel { - pub fn pretty<'a>( - &self, - allocator: &'a Arena<'a, ()>, - ) -> pretty::DocBuilder<'a, Arena<'a, ()>, ()> { - match self { - TopLevel::Function(name, args, _, expr) => allocator - .text("function") - .append(allocator.space()) - .append(allocator.text(name.current_name().to_string())) - .append(allocator.space()) - .append( - allocator - .intersperse( - args.iter().map(|(x, _)| allocator.text(x.to_string())), - allocator.text(","), - ) - .parens(), - ) - .append(allocator.space()) - .append(expr.pretty(allocator)), - - TopLevel::Statement(stmt) => stmt.pretty(allocator), - } - } -} - impl Expression { pub fn pretty<'a>( &self, @@ -267,7 +240,6 @@ impl TypeOrVar { } derived_display!(Program); -derived_display!(TopLevel); derived_display!(Expression); derived_display!(Primitive); derived_display!(Type); -- 2.53.0 From 212ca6cc5374d8a8aaca63da9c71fbdbb9be1880 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sun, 16 Jun 2024 20:59:32 -0700 Subject: [PATCH 57/59] CHECKPOINT: Initial syntax arbitrary implementation. --- Cargo.toml | 1 + src/bin/gen_program.rs | 23 + src/syntax.rs | 1 + src/syntax/arbitrary.rs | 984 ++++++++++++++++++++++++++++---------- src/syntax/ast.rs | 4 +- src/syntax/eval.rs | 2 + src/syntax/parser.lalrpop | 7 +- src/syntax/pretty.rs | 22 +- src/syntax/tokens.rs | 65 ++- src/syntax/validate.rs | 7 +- src/type_infer/convert.rs | 8 + src/util.rs | 1 + src/util/weighted_map.rs | 21 + 13 files changed, 867 insertions(+), 279 deletions(-) create mode 100644 src/bin/gen_program.rs create mode 100644 src/util/weighted_map.rs diff --git a/Cargo.toml b/Cargo.toml index 82314ea..2e5e0c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/bin/gen_program.rs b/src/bin/gen_program.rs new file mode 100644 index 0000000..58d656c --- /dev/null +++ b/src/bin/gen_program.rs @@ -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(()) +} diff --git a/src/syntax.rs b/src/syntax.rs index 15ee23a..dbbc97b 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -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; diff --git a/src/syntax/arbitrary.rs b/src/syntax/arbitrary.rs index 1d489a7..6c42c30 100644 --- a/src/syntax/arbitrary.rs +++ b/src/syntax/arbitrary.rs @@ -1,18 +1,136 @@ -use crate::syntax::ast::{ConstantType, Expression, Program, TopLevel, Value}; +use crate::syntax::ast::{ConstantType, Expression, TopLevel, Type, Value}; use crate::syntax::location::Location; use crate::syntax::name::Name; -use proptest::sample::select; +use crate::util::scoped_map::ScopedMap; +use crate::util::weighted_map::WeightedMap; +use proptest::prelude::Strategy; use proptest::strategy::{NewTree, ValueTree}; use proptest::test_runner::{TestRng, TestRunner}; -use proptest::{ - prelude::{Arbitrary, BoxedStrategy, Strategy}, - strategy::{Just, Union}, -}; -use std::collections::HashMap; -use std::ops::Range; +use rand::distributions::{Distribution, WeightedIndex}; +use rand::Rng; +use std::collections::{HashMap, VecDeque}; +use std::str::FromStr; +use std::sync::atomic::AtomicU64; pub const VALID_VARIABLE_NAMES: &str = r"[a-z][a-zA-Z0-9_]*"; +lazy_static::lazy_static! { + static ref BASE_PROGRAM_LENGTH: WeightedIndex = WeightedIndex::new([ + 0, // weight for 0 + 1, // weight for 1 + 1, // weight for 2 + 1, // weight for 3 + 3, // triple weight for 4 + 3, // triple weight for 5 + 3, // triple weight for 6 + 3, // triple weight for 7 + 3, // triple weight for 8 + 3, // triple weight for 9 + 3, // triple weight for 10 + 3, // double weight for 11 + 3, // double weight for 12 + 3, // double weight for 13 + 1, // weight for 14 + 1, // weight for 15 + ]).unwrap(); + + static ref KEEP_FIELD_TYPE_ANNOTATION: WeightedMap = WeightedMap::new(&[ + (3, true), + (1, false), + ]); + + static ref EXPRESSION_TYPE_FREQUENCIES: WeightedMap = WeightedMap::new(&[ + (1, ExpressionType::Value), + (1, ExpressionType::Constructor), + (1, ExpressionType::Reference), + (1, ExpressionType::FieldRef), + (1, ExpressionType::Cast), + (1, ExpressionType::Call), + (1, ExpressionType::Block), + (1, ExpressionType::Binding), + ]); + + static ref TYPE_FREQUENCIES: WeightedMap<&'static str> = WeightedMap::new(&[ + (1, "void"), + (5, "i8"), + (10, "i16"), + (5, "i32"), + (10, "i64"), + (5, "u8"), + (10, "u16"), + (5, "u32"), + (10, "u64"), + (1, ""), + (1, ""), + ]); + + static ref CALL_SHOULD_BE_PRIMITIVE: WeightedMap = WeightedMap::new(&[ + (3, true), + (1, false), + ]); + + static ref CALL_ARGUMENT_COUNT: WeightedIndex = WeightedIndex::new([ + 3, + 20, + 20, + 10, + 5, + 2, + 1, + 1, + 1, + ]).unwrap(); + + static ref BLOCK_LENGTH_MINUS_ONE: WeightedIndex = WeightedIndex::new([ + 5, + 10, + 20, + 20, + 10, + 10, + 5, + 1, + 1, + 1, + ]).unwrap(); + + static ref INNER_BLOCK_TYPE_SHOULD_BE_VOID: WeightedMap = WeightedMap::new(&[ + (1, true), + (1, false), + ]); + + static ref STRUCTURE_FIELD_COUNT: WeightedIndex = WeightedIndex::new([ + 0, // let's not mess around with empty structures here + 10, + 10, + 10, + 10, + 10, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + ]).unwrap(); +} + +#[derive(Clone, Debug)] +enum ExpressionType { + Value, + Constructor, + Reference, + FieldRef, + Cast, + Call, + Block, + Binding, +} + #[derive(Debug, Default)] pub struct ProgramGenerator {} @@ -21,29 +139,617 @@ impl Strategy for ProgramGenerator { type Value = Vec; fn new_tree(&self, runner: &mut TestRunner) -> NewTree { - unimplemented!() + NewTree::::Ok(ProgramTree::new(runner)) } } pub struct ProgramTree { _rng: TestRng, - current: Vec, + current: VecDeque, } +pub enum Requirement { + Function(Name, Vec, Type), + Structure(Name, HashMap), + Variable(Name, Type), +} + +type EstablishedStructMap = HashMap>; + impl ProgramTree { - fn new(mut rng: TestRng) -> Self { + fn new(runner: &mut TestRunner) -> Self { + let mut rng = runner.new_rng(); + let base_program_length = BASE_PROGRAM_LENGTH.sample(&mut rng); + let mut env = ScopedMap::new(); + let mut current = VecDeque::new(); + let mut established_structs = HashMap::new(); + + while current.len() < base_program_length { + let (expression, mut requirements) = + generate_expr(&mut rng, &mut established_structs, &mut env, None); + + current.push_front(TopLevel::Expression(expression)); + while let Some(requirement) = requirements.pop() { + match requirement { + Requirement::Function(name, args, result) => { + let (expression, newreqs) = generate_function( + &mut rng, + &mut established_structs, + &mut env, + name, + args, + result, + ); + current.push_front(TopLevel::Expression(expression)); + requirements.extend(newreqs.into_iter()); + } + + Requirement::Structure(name, fields) => { + let fields = fields + .into_iter() + .map(|(name, ty)| { + if KEEP_FIELD_TYPE_ANNOTATION.sample(&mut rng) { + (name, Some(ty)) + } else { + (name, None) + } + }) + .collect(); + current.push_front(TopLevel::Structure( + Location::manufactured(), + name, + fields, + )) + } + + Requirement::Variable(name, ty) => { + let (newexpr, newreqs) = + generate_expr(&mut rng, &mut established_structs, &mut env, Some(ty)); + let binding = + Expression::Binding(Location::manufactured(), name, Box::new(newexpr)); + current.push_front(TopLevel::Expression(binding)); + requirements.extend(newreqs.into_iter()); + } + } + } + } + ProgramTree { _rng: rng, - current: vec![], + current: current.into_iter().collect(), } } } +fn generate_expr( + rng: &mut TestRng, + established_struct_types: &mut EstablishedStructMap, + env: &mut ScopedMap, + optional_target_type: Option, +) -> (Expression, Vec) { + let target_type = + optional_target_type.unwrap_or_else(|| generate_type(rng, established_struct_types)); + let expression_type = EXPRESSION_TYPE_FREQUENCIES.sample(rng); + + match expression_type { + ExpressionType::Value => match target_type { + Type::Named(ref x) => match ConstantType::from_str(x.current_name()) { + Ok(ct) => (generate_constant(rng, ct), vec![]), + _ => generate_constructor(rng, established_struct_types, env, target_type), + }, + + Type::Struct(_) => { + generate_constructor(rng, established_struct_types, env, target_type) + } + }, + + ExpressionType::Constructor => { + generate_constructor(rng, established_struct_types, env, target_type) + } + + ExpressionType::Reference => { + let mut requirements = vec![]; + let mut available_variables = find_variables_with_type(env, &target_type); + + if available_variables.is_empty() { + let name = generate_name(rng); + requirements.push(Requirement::Variable(name.clone(), target_type.clone())); + available_variables.push(name); + } + + let idx = rng.gen_range(0..available_variables.len()); + let ref_name = available_variables.get(idx).expect("index in range"); + let expr = Expression::Reference(ref_name.clone()); + + (expr, requirements) + } + + ExpressionType::FieldRef => { + let mut requirements = vec![]; + + let mut valid_types = + find_structs_with_field_type(established_struct_types, &target_type); + if valid_types.is_empty() { + let name = generate_name(rng); + let mut fields = generate_structure_fields(rng, established_struct_types); + valid_types = fields + .iter() + .filter(|(_, t)| *t == &target_type) + .map(|(a, b)| (b.clone(), a.clone())) + .collect::>(); + + if valid_types.is_empty() { + let field_name = generate_name(rng); + fields.insert(field_name.clone(), target_type.clone()); + valid_types = vec![(Type::Named(name.clone()), field_name)]; + } + + requirements.push(Requirement::Structure(name.clone(), fields)); + } + + let new_target_type_idx = rng.gen_range(0..valid_types.len()); + let (new_target_type, name) = valid_types + .get(new_target_type_idx) + .expect("generated reasonable index") + .clone(); + let (subexp, newreqs) = + generate_expr(rng, established_struct_types, env, Some(new_target_type)); + requirements.extend(newreqs); + + ( + Expression::FieldRef(Location::manufactured(), Box::new(subexp), name), + requirements, + ) + } + + ExpressionType::Cast => match target_type { + Type::Named(ref name) => match ConstantType::from_str(name.current_name()) { + Ok(ct) => { + let new_targets = ct.safe_casts_to(); + let idx = rng.gen_range(0..new_targets.len()); + let new_constant_type = new_targets.get(idx).expect("generates in bounds"); + let new_type = Type::Named(Name::manufactured(new_constant_type.name())); + let (subexpr, reqs) = + generate_expr(rng, established_struct_types, env, Some(new_type)); + ( + Expression::Cast(Location::manufactured(), ct.name(), Box::new(subexpr)), + reqs, + ) + } + + Err(_) => { + let (subexpr, reqs) = generate_expr( + rng, + established_struct_types, + env, + Some(target_type.clone()), + ); + ( + Expression::Cast( + Location::manufactured(), + name.current_name().to_string(), + Box::new(subexpr), + ), + reqs, + ) + } + }, + + Type::Struct(fields) => match find_struct_for_fields(&fields, established_struct_types) + { + None => { + let name = generate_name(rng); + let mut new_fields = HashMap::new(); + + for (field_name, maybe_type) in fields.into_iter() { + let field_type = maybe_type + .unwrap_or_else(|| generate_type(rng, established_struct_types)); + new_fields.insert(field_name, field_type); + } + + established_struct_types.insert(name.clone(), new_fields.clone()); + let (subexpr, mut reqs) = generate_expr( + rng, + established_struct_types, + env, + Some(Type::Named(name.clone())), + ); + let result = Expression::Cast( + Location::manufactured(), + name.current_name().to_string(), + Box::new(subexpr), + ); + reqs.push(Requirement::Structure(name, new_fields)); + (result, reqs) + } + + Some((name, _)) => { + let (subexpr, reqs) = generate_expr( + rng, + established_struct_types, + env, + Some(Type::Named(name.clone())), + ); + let result = Expression::Cast( + Location::manufactured(), + name.current_name().to_string(), + Box::new(subexpr), + ); + (result, reqs) + } + }, + }, + + ExpressionType::Call => match target_type { + Type::Named(ref x) => match ConstantType::from_str(x.current_name()) { + Err(_) => { + genererate_call_to_function(rng, established_struct_types, env, target_type) + } + Ok(prim) if CALL_SHOULD_BE_PRIMITIVE.sample(rng) => { + let options = prim.primitives_for(); + let idx = rng.gen_range(0..options.len()); + let (primitive, argtypes) = options.get(idx).expect("index in range"); + let mut prereqs = vec![]; + let mut exprs = vec![]; + let func = Expression::Primitive( + Location::manufactured(), + Name::new(primitive, Location::manufactured()), + ); + + for possible_type in argtypes.iter() { + let (expr, new_prereqs) = generate_expr( + rng, + established_struct_types, + env, + possible_type + .map(|x| Type::Named(Name::new(x, Location::manufactured()))), + ); + exprs.push(expr); + prereqs.extend(new_prereqs.into_iter()); + } + + let retval = Expression::Call(Location::manufactured(), Box::new(func), exprs); + + (retval, prereqs) + } + Ok(_) => { + genererate_call_to_function(rng, established_struct_types, env, target_type) + } + }, + + Type::Struct(_) => { + genererate_call_to_function(rng, established_struct_types, env, target_type) + } + }, + + ExpressionType::Block => { + let target_block_size_minus_one = BLOCK_LENGTH_MINUS_ONE.sample(rng); + let mut block = VecDeque::new(); + let mut prereqs = vec![]; + + while block.len() < target_block_size_minus_one { + let inner_type = if INNER_BLOCK_TYPE_SHOULD_BE_VOID.sample(rng) { + Some(Type::Named(Name::new("void", Location::manufactured()))) + } else { + Some(generate_type(rng, established_struct_types)) + }; + let (expr, new_prereqs) = + generate_expr(rng, established_struct_types, env, inner_type); + block.push_back(expr); + + let mut new_work_queue: VecDeque = new_prereqs.into_iter().collect(); + + while let Some(next) = new_work_queue.pop_front() { + if let Requirement::Variable(name, varty) = next { + let (value, even_newer_reqs) = + generate_expr(rng, established_struct_types, env, Some(varty)); + + block.push_front(Expression::Binding( + Location::manufactured(), + name, + Box::new(value), + )); + for req in even_newer_reqs.into_iter() { + new_work_queue.push_front(req); + } + } else { + prereqs.push(next); + } + } + } + + let retval = Expression::Block(Location::manufactured(), block.into_iter().collect()); + (retval, prereqs) + } + + ExpressionType::Binding => { + let name = generate_name(rng); + let (expr, prereqs) = generate_expr( + rng, + established_struct_types, + env, + Some(target_type.clone()), + ); + env.insert(name.clone(), target_type); + ( + Expression::Binding(Location::manufactured(), name, Box::new(expr)), + prereqs, + ) + } + } +} + +fn generate_constant(rng: &mut TestRng, ct: ConstantType) -> Expression { + let build = + |val: u64| Expression::Value(Location::manufactured(), Value::Number(None, Some(ct), val)); + + match ct { + ConstantType::I8 => build(rng.gen::() as u64), + ConstantType::I16 => build(rng.gen::() as u64), + ConstantType::I32 => build(rng.gen::() as u64), + ConstantType::I64 => build(rng.gen::() as u64), + ConstantType::U8 => build(rng.gen::() as u64), + ConstantType::U16 => build(rng.gen::() as u64), + ConstantType::U32 => build(rng.gen::() as u64), + ConstantType::U64 => build(rng.gen::()), + ConstantType::Void => Expression::Value(Location::manufactured(), Value::Void), + } +} + +fn generate_constructor( + rng: &mut TestRng, + established_struct_types: &mut EstablishedStructMap, + env: &mut ScopedMap, + target_type: Type, +) -> (Expression, Vec) { + match target_type { + Type::Named(x) => match ConstantType::from_str(x.current_name()) { + Ok(ct) => (generate_constant(rng, ct), vec![]), + Err(_) => { + let mut field_assignments = vec![]; + let mut requirements = vec![]; + + let fields = match established_struct_types.get(&x) { + Some(fields) => fields.clone(), + None => { + let fields = generate_structure_fields(rng, established_struct_types); + requirements.push(Requirement::Structure(x.clone(), fields.clone())); + established_struct_types.insert(x.clone(), fields.clone()); + fields + } + }; + + for (field_name, field_type) in fields.into_iter() { + let (subexpr, reqs) = + generate_expr(rng, established_struct_types, env, Some(field_type)); + requirements.extend(reqs.into_iter()); + field_assignments.push((field_name, subexpr)); + } + + let result = + Expression::Constructor(Location::manufactured(), x, field_assignments); + + (result, requirements) + } + }, + + Type::Struct(fields) => { + let mut requirements = vec![]; + + let (name, fields) = match find_struct_for_fields(&fields, established_struct_types) { + Some((name, fields)) => (name, fields), + None => { + let new_name = generate_name(rng); + let mut result_fields = HashMap::new(); + + for (field, opttype) in fields { + let field_type = opttype + .clone() + .unwrap_or_else(|| generate_type(rng, established_struct_types)); + + result_fields.insert(field.clone(), field_type); + } + + requirements.push(Requirement::Structure( + new_name.clone(), + result_fields.clone(), + )); + (new_name, result_fields) + } + }; + + let mut field_assignments = vec![]; + for (field_name, field_type) in fields.into_iter() { + let (subexpr, reqs) = + generate_expr(rng, established_struct_types, env, Some(field_type)); + requirements.extend(reqs.into_iter()); + field_assignments.push((field_name, subexpr)); + } + + let result = Expression::Constructor(Location::manufactured(), name, field_assignments); + (result, requirements) + } + } +} + +fn generate_function( + rng: &mut TestRng, + established_struct_types: &mut EstablishedStructMap, + env: &mut ScopedMap, + name: Name, + arg_types: Vec, + ret: Type, +) -> (Expression, Vec) { + let mut args = vec![]; + + env.new_scope(); + for arg_type in arg_types.into_iter() { + let arg_name = generate_name(rng); + args.push((arg_name.clone(), Some(arg_type.clone()))); + env.insert(arg_name, arg_type); + } + let (body, prereqs) = generate_expr(rng, established_struct_types, env, Some(ret.clone())); + env.release_scope(); + + let function = Expression::Function( + Location::manufactured(), + Some(name), + args, + Some(ret), + Box::new(body), + ); + + (function, prereqs) +} + +fn generate_structure_fields( + rng: &mut TestRng, + established_struct_types: &mut EstablishedStructMap, +) -> HashMap { + let mut fields = HashMap::new(); + + for _ in 0..STRUCTURE_FIELD_COUNT.sample(rng) { + let name = generate_name(rng); + let ty = generate_type(rng, established_struct_types); + fields.insert(name, ty); + } + + fields +} + +fn find_struct_for_fields( + found_fields: &[(Name, Option)], + established_struct_types: &EstablishedStructMap, +) -> Option<(Name, HashMap)> { + 'top_search: for (name, established_fields) in established_struct_types.iter() { + if found_fields.len() == established_fields.len() { + for (found_name, found_ty) in found_fields.iter() { + match established_fields.get(found_name) { + None => continue 'top_search, + Some(established_ty) => { + if let Some(found_ty) = found_ty { + if found_ty != established_ty { + continue 'top_search; + } + } + } + } + } + return Some((name.clone(), established_fields.clone())); + } + } + + None +} + +fn generate_name(rng: &mut TestRng) -> Name { + static COUNTER: AtomicU64 = AtomicU64::new(0); + + let idx = rng.gen_range(0..names::NOUNS.len()); + let name = format!( + "{}{}", + names::NOUNS[idx], + COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + ); + + Name::new(name, Location::manufactured()) +} + +fn generate_type(rng: &mut TestRng, established_struct_types: &mut EstablishedStructMap) -> Type { + let possible_result = TYPE_FREQUENCIES.sample(rng); + + match possible_result { + "" if !established_struct_types.is_empty() => { + let keys: Vec<&Name> = established_struct_types.keys().collect(); + let idx = rng.gen_range(0..keys.len()); + let key = *keys.get(idx).expect("index in range"); + Type::Named(Name::new(key.clone(), Location::manufactured())) + } + + "" | "" => { + let fields_map = generate_structure_fields(rng, established_struct_types); + let mut fields = vec![]; + + for (name, ty) in fields_map.into_iter() { + if KEEP_FIELD_TYPE_ANNOTATION.sample(rng) { + fields.push((name, Some(ty))); + } else { + fields.push((name, None)); + } + } + + Type::Struct(fields) + } + + _ => Type::Named(Name::new(possible_result, Location::manufactured())), + } +} + +fn find_structs_with_field_type( + established_struct_types: &mut EstablishedStructMap, + target_type: &Type, +) -> Vec<(Type, Name)> { + let mut results = vec![]; + + for (est_name, est_fields) in established_struct_types.iter() { + for (field_name, field_type) in est_fields.iter() { + if target_type == field_type { + results.push((Type::Named(est_name.clone()), field_name.clone())); + } + } + } + + results +} + +fn find_variables_with_type(env: &ScopedMap, target_type: &Type) -> Vec { + let mut results = vec![]; + + for (name, ty) in env.bindings().into_iter() { + if target_type == &ty { + results.push(name); + } + } + + results +} + +fn genererate_call_to_function( + rng: &mut TestRng, + established_struct_types: &mut EstablishedStructMap, + env: &mut ScopedMap, + target_type: Type, +) -> (Expression, Vec) { + let name = generate_name(rng); + let arg_count = CALL_ARGUMENT_COUNT.sample(rng); + let mut prereqs = vec![]; + let mut arg_types = vec![]; + let mut arg_exprs = vec![]; + + for _ in 0..arg_count { + let arg_type = generate_type(rng, established_struct_types); + let (arg_expr, new_reqs) = + generate_expr(rng, established_struct_types, env, Some(arg_type.clone())); + arg_types.push(arg_type); + arg_exprs.push(arg_expr); + prereqs.extend(new_reqs.into_iter()); + } + + let call = Expression::Call( + Location::manufactured(), + Box::new(Expression::Reference(name.clone())), + arg_exprs, + ); + prereqs.push(Requirement::Function(name, arg_types, target_type)); + + (call, prereqs) +} + impl ValueTree for ProgramTree { type Value = Vec; fn current(&self) -> Self::Value { - self.current.clone() + self.current.iter().cloned().collect() } fn simplify(&mut self) -> bool { @@ -54,255 +760,3 @@ impl ValueTree for ProgramTree { false } } - -//impl ConstantType { -// fn get_operators(&self) -> &'static [(&'static str, usize)] { -// match self { -// ConstantType::Void => &[], -// ConstantType::I8 | ConstantType::I16 | ConstantType::I32 | ConstantType::I64 => { -// &[("+", 2), ("negate", 1), ("-", 2), ("*", 2), ("/", 2)] -// } -// ConstantType::U8 | ConstantType::U16 | ConstantType::U32 | ConstantType::U64 => { -// &[("+", 2), ("-", 2), ("*", 2), ("/", 2)] -// } -// } -// } -//} -// -//#[derive(Clone)] -//pub struct GenerationEnvironment { -// allow_inference: bool, -// block_length: Range, -// bindings: HashMap, -// return_type: ConstantType, -//} -// -//impl Default for GenerationEnvironment { -// fn default() -> Self { -// GenerationEnvironment { -// allow_inference: true, -// block_length: 2..10, -// bindings: HashMap::new(), -// return_type: ConstantType::U64, -// } -// } -//} -// -//impl GenerationEnvironment { -// pub fn new(allow_inference: bool) -> Self { -// GenerationEnvironment { -// allow_inference, -// ..Default::default() -// } -// } -//} -// -//impl Arbitrary for Program { -// type Parameters = GenerationEnvironment; -// type Strategy = BoxedStrategy; -// -// fn arbitrary_with(genenv: Self::Parameters) -> Self::Strategy { -// proptest::collection::vec( -// ProgramTopLevelInfo::arbitrary(), -// genenv.block_length.clone(), -// ) -// .prop_flat_map(move |mut ptlis| { -// let mut items = Vec::new(); -// let mut genenv = genenv.clone(); -// -// for psi in ptlis.drain(..) { -// if genenv.bindings.is_empty() || psi.should_be_binding { -// genenv.return_type = psi.binding_type; -// let expr = Expression::arbitrary_with(genenv.clone()); -// genenv.bindings.insert(psi.name.clone(), psi.binding_type); -// items.push( -// expr.prop_map(move |expr| { -// TopLevel::Expression(Expression::Binding( -// Location::manufactured(), -// psi.name.clone(), -// Box::new(expr), -// )) -// }) -// .boxed(), -// ); -// } else { -// let printers = genenv.bindings.keys().map(|n| { -// Just(TopLevel::Expression(Expression::Call( -// Location::manufactured(), -// Box::new(Expression::Primitive( -// Location::manufactured(), -// Name::manufactured("print"), -// )), -// vec![Expression::Reference(n.clone())], -// ))) -// }); -// items.push(Union::new(printers).boxed()); -// } -// } -// -// items -// .prop_map(|items| Program { -// functions: HashMap::new(), -// structures: HashMap::new(), -// body: unimplemented!(), -// }) -// .boxed() -// }) -// .boxed() -// } -//} -// -//impl Arbitrary for Name { -// type Parameters = (); -// type Strategy = BoxedStrategy; -// -// fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { -// VALID_VARIABLE_NAMES.prop_map(Name::manufactured).boxed() -// } -//} -// -//#[derive(Debug)] -//struct ProgramTopLevelInfo { -// should_be_binding: bool, -// name: Name, -// binding_type: ConstantType, -//} -// -//impl Arbitrary for ProgramTopLevelInfo { -// type Parameters = (); -// type Strategy = BoxedStrategy; -// -// fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { -// ( -// Union::new(vec![Just(true), Just(true), Just(false)]), -// Name::arbitrary(), -// ConstantType::arbitrary(), -// ) -// .prop_map( -// |(should_be_binding, name, binding_type)| ProgramTopLevelInfo { -// should_be_binding, -// name, -// binding_type, -// }, -// ) -// .boxed() -// } -//} -// -//impl Arbitrary for Expression { -// type Parameters = GenerationEnvironment; -// type Strategy = BoxedStrategy; -// -// fn arbitrary_with(genenv: Self::Parameters) -> Self::Strategy { -// // Value(Location, Value). These are the easiest variations to create, because we can always -// // create one. -// let value_strategy = Value::arbitrary_with(genenv.clone()) -// .prop_map(|x| Expression::Value(Location::manufactured(), x)) -// .boxed(); -// -// // Reference(Location, String), These are slightly trickier, because we can end up in a situation -// // where either no variables are defined, or where none of the defined variables have a type we -// // can work with. So what we're going to do is combine this one with the previous one as a "leaf -// // strategy" -- our non-recursive items -- if we can, or just set that to be the value strategy -// // if we can't actually create an references. -// let mut bound_variables_of_type = genenv -// .bindings -// .iter() -// .filter(|(_, v)| genenv.return_type == **v) -// .map(|(n, _)| n) -// .collect::>(); -// let leaf_strategy = if bound_variables_of_type.is_empty() { -// value_strategy -// } else { -// let mut strats = bound_variables_of_type -// .drain(..) -// .map(|x| Just(Expression::Reference(x.clone())).boxed()) -// .collect::>(); -// strats.push(value_strategy); -// Union::new(strats).boxed() -// }; -// -// // now we generate our recursive types, given our leaf strategy -// leaf_strategy -// .prop_recursive(3, 10, 2, move |strat| { -// ( -// select(genenv.return_type.get_operators()), -// strat.clone(), -// strat, -// ) -// .prop_map(|((oper, count), left, right)| { -// let mut args = vec![left, right]; -// while args.len() > count { -// args.pop(); -// } -// Expression::Call( -// Location::manufactured(), -// Box::new(Expression::Primitive( -// Location::manufactured(), -// Name::manufactured(oper), -// )), -// args, -// ) -// }) -// }) -// .boxed() -// } -//} -// -//impl Arbitrary for Value { -// type Parameters = GenerationEnvironment; -// type Strategy = BoxedStrategy; -// -// fn arbitrary_with(genenv: Self::Parameters) -> Self::Strategy { -// let printed_base_strategy = Union::new([ -// Just(None::), -// Just(Some(2)), -// Just(Some(8)), -// Just(Some(10)), -// Just(Some(16)), -// ]); -// let value_strategy = u64::arbitrary(); -// -// (printed_base_strategy, bool::arbitrary(), value_strategy) -// .prop_map(move |(base, declare_type, value)| { -// let converted_value = match genenv.return_type { -// ConstantType::Void => value, -// ConstantType::I8 => value % (i8::MAX as u64), -// ConstantType::U8 => value % (u8::MAX as u64), -// ConstantType::I16 => value % (i16::MAX as u64), -// ConstantType::U16 => value % (u16::MAX as u64), -// ConstantType::I32 => value % (i32::MAX as u64), -// ConstantType::U32 => value % (u32::MAX as u64), -// ConstantType::I64 => value % (i64::MAX as u64), -// ConstantType::U64 => value, -// }; -// let ty = if declare_type || !genenv.allow_inference { -// Some(genenv.return_type) -// } else { -// None -// }; -// Value::Number(base, ty, converted_value) -// }) -// .boxed() -// } -//} -// -//impl Arbitrary for ConstantType { -// type Parameters = (); -// type Strategy = BoxedStrategy; -// -// fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { -// Union::new([ -// Just(ConstantType::I8), -// Just(ConstantType::I16), -// Just(ConstantType::I32), -// Just(ConstantType::I64), -// Just(ConstantType::U8), -// Just(ConstantType::U16), -// Just(ConstantType::U32), -// Just(ConstantType::U64), -// ]) -// .boxed() -// } -//} -// diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs index 7fb24e9..56ed690 100644 --- a/src/syntax/ast.rs +++ b/src/syntax/ast.rs @@ -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)>), } 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, Option, u64), + /// The empty value + Void, } #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/src/syntax/eval.rs b/src/syntax/eval.rs index e07a876..30c51a3 100644 --- a/src/syntax/eval.rs +++ b/src/syntax/eval.rs @@ -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) => { diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index 2a9516a..6455827 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -92,9 +92,12 @@ Structure: TopLevel = { } } -Field: (Name, Type) = { +Field: (Name, Option) = { "> ":" ";" => - (Name::new(name, Location::new(file_idx, s..e)), field_type) + (Name::new(name, Location::new(file_idx, s..e)), Some(field_type)), + "> ";" => + (Name::new(name, Location::new(file_idx, s..e)), None), + } Type: Type = { diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index e184aaf..36deeab 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -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(""), } } } diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs index 9ad3e09..688ac42 100644 --- a/src/syntax/tokens.rs +++ b/src/syntax/tokens.rs @@ -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 for cranelift_codegen::ir::Type { } } +pub struct StringNotConstantType(); + +impl FromStr for ConstantType { + type Err = StringNotConstantType; + + fn from_str(s: &str) -> Result { + 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 { @@ -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>)> { + 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)] diff --git a/src/syntax/validate.rs b/src/syntax/validate.rs index 8655643..c754101 100644 --- a/src/syntax/validate.rs +++ b/src/syntax/validate.rs @@ -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); } diff --git a/src/type_infer/convert.rs b/src/type_infer/convert.rs index 1c0060d..57c3e15 100644 --- a/src/type_infer/convert.rs +++ b/src/type_infer/convert.rs @@ -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 => { diff --git a/src/util.rs b/src/util.rs index d872744..d2217e3 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,3 +1,4 @@ pub mod pretty; pub mod scoped_map; pub mod warning_result; +pub mod weighted_map; diff --git a/src/util/weighted_map.rs b/src/util/weighted_map.rs new file mode 100644 index 0000000..48a9aa8 --- /dev/null +++ b/src/util/weighted_map.rs @@ -0,0 +1,21 @@ +use rand::distributions::{Distribution, WeightedIndex}; + +pub struct WeightedMap { + index: WeightedIndex, + items: Vec, +} + +impl WeightedMap { + 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 Distribution for WeightedMap { + fn sample(&self, rng: &mut R) -> T { + let idx = self.index.sample(rng); + self.items.get(idx).unwrap().clone() + } +} -- 2.53.0 From 6deccd5529243b04641805d65893a01523b6035c Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Wed, 19 Jun 2024 09:47:37 -0700 Subject: [PATCH 58/59] Get some indenting right. --- src/syntax/arbitrary.rs | 125 +++++++++++++++++++++++++++++++--------- src/syntax/pretty.rs | 105 +++++++++++++++++++++------------ 2 files changed, 165 insertions(+), 65 deletions(-) diff --git a/src/syntax/arbitrary.rs b/src/syntax/arbitrary.rs index 6c42c30..5edc698 100644 --- a/src/syntax/arbitrary.rs +++ b/src/syntax/arbitrary.rs @@ -13,6 +13,7 @@ use std::str::FromStr; use std::sync::atomic::AtomicU64; pub const VALID_VARIABLE_NAMES: &str = r"[a-z][a-zA-Z0-9_]*"; +const MAX_COMPLEX_DEPTH: usize = 4; lazy_static::lazy_static! { static ref BASE_PROGRAM_LENGTH: WeightedIndex = WeightedIndex::new([ @@ -50,6 +51,11 @@ lazy_static::lazy_static! { (1, ExpressionType::Binding), ]); + static ref SIMPLIFIED_EXPRESSION_TYPES: WeightedMap = WeightedMap::new(&[ + (1, ExpressionType::Reference), + (1, ExpressionType::Value), + ]); + static ref TYPE_FREQUENCIES: WeightedMap<&'static str> = WeightedMap::new(&[ (1, "void"), (5, "i8"), @@ -166,13 +172,14 @@ impl ProgramTree { while current.len() < base_program_length { let (expression, mut requirements) = - generate_expr(&mut rng, &mut established_structs, &mut env, None); + generate_expr(0, &mut rng, &mut established_structs, &mut env, None); current.push_front(TopLevel::Expression(expression)); while let Some(requirement) = requirements.pop() { match requirement { Requirement::Function(name, args, result) => { let (expression, newreqs) = generate_function( + 1, &mut rng, &mut established_structs, &mut env, @@ -203,8 +210,13 @@ impl ProgramTree { } Requirement::Variable(name, ty) => { - let (newexpr, newreqs) = - generate_expr(&mut rng, &mut established_structs, &mut env, Some(ty)); + let (newexpr, newreqs) = generate_expr( + 1, + &mut rng, + &mut established_structs, + &mut env, + Some(ty), + ); let binding = Expression::Binding(Location::manufactured(), name, Box::new(newexpr)); current.push_front(TopLevel::Expression(binding)); @@ -222,6 +234,7 @@ impl ProgramTree { } fn generate_expr( + depth: usize, rng: &mut TestRng, established_struct_types: &mut EstablishedStructMap, env: &mut ScopedMap, @@ -229,22 +242,26 @@ fn generate_expr( ) -> (Expression, Vec) { let target_type = optional_target_type.unwrap_or_else(|| generate_type(rng, established_struct_types)); - let expression_type = EXPRESSION_TYPE_FREQUENCIES.sample(rng); + let expression_type = if depth > MAX_COMPLEX_DEPTH { + SIMPLIFIED_EXPRESSION_TYPES.sample(rng) + } else { + EXPRESSION_TYPE_FREQUENCIES.sample(rng) + }; match expression_type { ExpressionType::Value => match target_type { Type::Named(ref x) => match ConstantType::from_str(x.current_name()) { Ok(ct) => (generate_constant(rng, ct), vec![]), - _ => generate_constructor(rng, established_struct_types, env, target_type), + _ => generate_constructor(depth, rng, established_struct_types, env, target_type), }, Type::Struct(_) => { - generate_constructor(rng, established_struct_types, env, target_type) + generate_constructor(depth, rng, established_struct_types, env, target_type) } }, ExpressionType::Constructor => { - generate_constructor(rng, established_struct_types, env, target_type) + generate_constructor(depth, rng, established_struct_types, env, target_type) } ExpressionType::Reference => { @@ -292,8 +309,13 @@ fn generate_expr( .get(new_target_type_idx) .expect("generated reasonable index") .clone(); - let (subexp, newreqs) = - generate_expr(rng, established_struct_types, env, Some(new_target_type)); + let (subexp, newreqs) = generate_expr( + depth + 1, + rng, + established_struct_types, + env, + Some(new_target_type), + ); requirements.extend(newreqs); ( @@ -309,8 +331,13 @@ fn generate_expr( let idx = rng.gen_range(0..new_targets.len()); let new_constant_type = new_targets.get(idx).expect("generates in bounds"); let new_type = Type::Named(Name::manufactured(new_constant_type.name())); - let (subexpr, reqs) = - generate_expr(rng, established_struct_types, env, Some(new_type)); + let (subexpr, reqs) = generate_expr( + depth + 1, + rng, + established_struct_types, + env, + Some(new_type), + ); ( Expression::Cast(Location::manufactured(), ct.name(), Box::new(subexpr)), reqs, @@ -319,6 +346,7 @@ fn generate_expr( Err(_) => { let (subexpr, reqs) = generate_expr( + depth + 1, rng, established_struct_types, env, @@ -349,6 +377,7 @@ fn generate_expr( established_struct_types.insert(name.clone(), new_fields.clone()); let (subexpr, mut reqs) = generate_expr( + depth + 1, rng, established_struct_types, env, @@ -365,6 +394,7 @@ fn generate_expr( Some((name, _)) => { let (subexpr, reqs) = generate_expr( + depth + 1, rng, established_struct_types, env, @@ -382,9 +412,13 @@ fn generate_expr( ExpressionType::Call => match target_type { Type::Named(ref x) => match ConstantType::from_str(x.current_name()) { - Err(_) => { - genererate_call_to_function(rng, established_struct_types, env, target_type) - } + Err(_) => genererate_call_to_function( + depth, + rng, + established_struct_types, + env, + target_type, + ), Ok(prim) if CALL_SHOULD_BE_PRIMITIVE.sample(rng) => { let options = prim.primitives_for(); let idx = rng.gen_range(0..options.len()); @@ -398,6 +432,7 @@ fn generate_expr( for possible_type in argtypes.iter() { let (expr, new_prereqs) = generate_expr( + depth + 1, rng, established_struct_types, env, @@ -412,13 +447,17 @@ fn generate_expr( (retval, prereqs) } - Ok(_) => { - genererate_call_to_function(rng, established_struct_types, env, target_type) - } + Ok(_) => genererate_call_to_function( + depth, + rng, + established_struct_types, + env, + target_type, + ), }, Type::Struct(_) => { - genererate_call_to_function(rng, established_struct_types, env, target_type) + genererate_call_to_function(depth, rng, established_struct_types, env, target_type) } }, @@ -434,15 +473,20 @@ fn generate_expr( Some(generate_type(rng, established_struct_types)) }; let (expr, new_prereqs) = - generate_expr(rng, established_struct_types, env, inner_type); + generate_expr(depth + 1, rng, established_struct_types, env, inner_type); block.push_back(expr); let mut new_work_queue: VecDeque = new_prereqs.into_iter().collect(); while let Some(next) = new_work_queue.pop_front() { if let Requirement::Variable(name, varty) = next { - let (value, even_newer_reqs) = - generate_expr(rng, established_struct_types, env, Some(varty)); + let (value, even_newer_reqs) = generate_expr( + depth + 1, + rng, + established_struct_types, + env, + Some(varty), + ); block.push_front(Expression::Binding( Location::manufactured(), @@ -465,6 +509,7 @@ fn generate_expr( ExpressionType::Binding => { let name = generate_name(rng); let (expr, prereqs) = generate_expr( + depth + 1, rng, established_struct_types, env, @@ -497,6 +542,7 @@ fn generate_constant(rng: &mut TestRng, ct: ConstantType) -> Expression { } fn generate_constructor( + depth: usize, rng: &mut TestRng, established_struct_types: &mut EstablishedStructMap, env: &mut ScopedMap, @@ -520,8 +566,13 @@ fn generate_constructor( }; for (field_name, field_type) in fields.into_iter() { - let (subexpr, reqs) = - generate_expr(rng, established_struct_types, env, Some(field_type)); + let (subexpr, reqs) = generate_expr( + depth + 1, + rng, + established_struct_types, + env, + Some(field_type), + ); requirements.extend(reqs.into_iter()); field_assignments.push((field_name, subexpr)); } @@ -560,8 +611,13 @@ fn generate_constructor( let mut field_assignments = vec![]; for (field_name, field_type) in fields.into_iter() { - let (subexpr, reqs) = - generate_expr(rng, established_struct_types, env, Some(field_type)); + let (subexpr, reqs) = generate_expr( + depth + 1, + rng, + established_struct_types, + env, + Some(field_type), + ); requirements.extend(reqs.into_iter()); field_assignments.push((field_name, subexpr)); } @@ -573,6 +629,7 @@ fn generate_constructor( } fn generate_function( + depth: usize, rng: &mut TestRng, established_struct_types: &mut EstablishedStructMap, env: &mut ScopedMap, @@ -588,7 +645,13 @@ fn generate_function( args.push((arg_name.clone(), Some(arg_type.clone()))); env.insert(arg_name, arg_type); } - let (body, prereqs) = generate_expr(rng, established_struct_types, env, Some(ret.clone())); + let (body, prereqs) = generate_expr( + depth + 1, + rng, + established_struct_types, + env, + Some(ret.clone()), + ); env.release_scope(); let function = Expression::Function( @@ -715,6 +778,7 @@ fn find_variables_with_type(env: &ScopedMap, target_type: &Type) -> } fn genererate_call_to_function( + depth: usize, rng: &mut TestRng, established_struct_types: &mut EstablishedStructMap, env: &mut ScopedMap, @@ -728,8 +792,13 @@ fn genererate_call_to_function( for _ in 0..arg_count { let arg_type = generate_type(rng, established_struct_types); - let (arg_expr, new_reqs) = - generate_expr(rng, established_struct_types, env, Some(arg_type.clone())); + let (arg_expr, new_reqs) = generate_expr( + depth + 1, + rng, + established_struct_types, + env, + Some(arg_type.clone()), + ); arg_types.push(arg_type); arg_exprs.push(arg_expr); prereqs.extend(new_reqs.into_iter()); diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index 36deeab..1d92c8a 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -7,35 +7,37 @@ impl Program { let mut result = allocator.nil(); for definition in self.structures.values() { - result = result - .append(allocator.text("struct")) + let mut interior = allocator.nil(); + + for (name, ty) in definition.fields.iter() { + let mut type_bit = allocator.nil(); + + if let Some(ty) = ty { + type_bit = allocator + .text(":") + .append(allocator.space()) + .append(ty.pretty(allocator)); + } + + interior = interior + .append(name.original_name().to_string()) + .append(type_bit) + .append(allocator.text(";")) + .append(allocator.hardline()); + } + + interior = interior.indent(9); + + let start = allocator.text("struct") .append(allocator.space()) .append(allocator.text(definition.name.original_name().to_string())) .append(allocator.space()) - .append(allocator.text("{")) - .append(allocator.hardline()) - .append( - allocator - .concat(definition.fields.iter().map(|(name, ty)| { - let mut type_bit = allocator.nil(); + .append(allocator.text("{")); - if let Some(ty) = ty { - type_bit = allocator - .text(":") - .append(allocator.space()) - .append(ty.pretty(allocator)); - } - - allocator - .text(name.original_name().to_string()) - .append(type_bit) - .append(allocator.text(";")) - .append(allocator.hardline()) - })) - .nest(2), - ) - .append(allocator.text("}")) + let conclusion = allocator.text("}") .append(allocator.hardline()); + + result = result.append(start.append(interior).append(conclusion)); } for definition in self.functions.values() { @@ -111,8 +113,7 @@ impl TopLevel { .append(type_bit) .append(allocator.text(";")) .append(allocator.hardline()) - })) - .nest(2), + })).indent(2) ) .append(allocator.text("}")) .append(allocator.hardline()), @@ -127,6 +128,8 @@ impl Expression { Expression::Constructor(_, name, fields) => allocator .text(name.to_string()) .append(allocator.space()) + .append(allocator.text("{")) + .append(allocator.hardline()) .append( allocator .concat(fields.iter().map(|(n, e)| { @@ -138,9 +141,9 @@ impl Expression { .append(allocator.text(";")) .append(allocator.hardline()) })) - .nest(2) - .braces(), - ), + .indent(2) + ) + .append(allocator.text("}")), Expression::Reference(var) => allocator.text(var.to_string()), Expression::FieldRef(_, val, field) => val .pretty(allocator) @@ -152,27 +155,55 @@ impl Expression { .append(e.pretty(allocator)), Expression::Primitive(_, op) => allocator.text(op.original_name().to_string()), Expression::Call(_, fun, args) => { - let args = args.iter().map(|x| x.pretty(allocator)); - let comma_sepped_args = allocator.intersperse(args, allocator.text(",")); - fun.pretty(allocator).append(comma_sepped_args.parens()) + let mut args = args.iter().map(|x| x.pretty(allocator)).collect::>(); + + match fun.as_ref() { + Expression::Primitive(_, name) if ["/", "*", "+", "-"].contains(&name.current_name()) && args.len() == 2 => { + let second = args.pop().unwrap(); + args.pop() + .unwrap() + .append(allocator.space()) + .append(allocator.text(name.current_name().to_string())) + .append(allocator.space()) + .append(second) + .parens() + } + + Expression::Primitive(_, name) if ["negate"].contains(&name.current_name()) && args.len() == 1 => + allocator.text("-").append(args.pop().unwrap()), + + Expression::Primitive(_, name) if ["print"].contains(&&name.current_name()) && args.len() == 1 => + allocator.text("print") + .append(allocator.space()) + .append(args.pop().unwrap()), + + _ => { + let comma_sepped_args = allocator.intersperse(args, allocator.text(",")); + fun.pretty(allocator).append(comma_sepped_args.parens()) + } + } } Expression::Block(_, stmts) => match stmts.split_last() { None => allocator.text("()"), Some((last, &[])) => last.pretty(allocator), Some((last, start)) => { - let mut result = allocator.text("{").append(allocator.hardline()); + let beginning = allocator.text("{").append(allocator.hardline()); + let mut inner = allocator.nil(); for stmt in start.iter() { - result = result + inner = inner .append(stmt.pretty(allocator)) .append(allocator.text(";")) .append(allocator.hardline()); } - result + inner = inner .append(last.pretty(allocator)) - .append(allocator.hardline()) - .append(allocator.text("}")) + .append(allocator.hardline()); + + inner = inner.indent(2); + + beginning.append(inner).append(allocator.text("}")) } }, Expression::Binding(_, var, expr) => allocator -- 2.53.0 From b72b2eddb469577549f7fd02e0b7359f437c49f6 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Mon, 21 Oct 2024 09:41:57 -0700 Subject: [PATCH 59/59] broken --- examples/basic/generated0005.ngr | 11284 +++++++++++++++++++ src/bin/{gen_program.rs => gen-program.rs} | 0 src/ir/arbitrary.rs | 18 +- src/syntax.rs | 45 +- src/syntax/arbitrary.rs | 89 +- src/syntax/parser.lalrpop | 2 + src/syntax/pretty.rs | 44 +- 7 files changed, 11409 insertions(+), 73 deletions(-) create mode 100644 examples/basic/generated0005.ngr rename src/bin/{gen_program.rs => gen-program.rs} (100%) diff --git a/examples/basic/generated0005.ngr b/examples/basic/generated0005.ngr new file mode 100644 index 0000000..d9bd038 --- /dev/null +++ b/examples/basic/generated0005.ngr @@ -0,0 +1,11284 @@ +struct Picture29 { + start30: u64; + girls31: struct {twist5: u64; + poison2: _; + disease4: _; + pickle12: _; + drop14: _; + father9: u64; + size10: i16; + watch7: i32; + temper28: _; + crown13: _; + pizzas6: u32; + trip3: u64; + earthquake8: u8; + fight15: struct {territory16: _; + camp18: i16; + action22: u16; + grade27: u8; + letter26: u32; + surprise17: _; + fold24: i64; + mice23: _; + stranger25: i64; + pickle20: u64; + channel21: _; + love19: _;}; + direction11: i64;}; +} +struct Judge45 { + dust50; + pleasure69: u32; + babies48: u8; + icicle46: i16; + tray49: u8; + flavor70: i8; + stew71: i32; + order47: i8; + flame51: i16; + adjustment52: struct {sister54: struct {scarf59: i16; + grip61: i16; + change60: _; + song57: u64; + stamp58: _; + nerve55: i16; + lamp62: _; + trade63: void; + sponge64: _; + downtown65: u16; + breath56: _;}; + cart68: i16; + van53: u16; + guitar66: u32; + ghost67: _;}; +} +struct North85 { + day87: i32; + brake86: i16; + apple88; +} +struct Wren2978 { + juice2960: u64; + plant2970: u64; + bedroom2959: i16; + plough2957: u32; + kettle2974; + land2963; + business2962; + toys2961: u8; + sidewalk2958: i16; + doll2972: i16; + insect2964: struct {haircut2969: i64; + clam2965: u16; + kiss2966: i16; + cry2968: u32; + airplane2967: void;}; + snails2973: u16; + pickle2975: Answer1090; + fire2971: u64; +} +sheet2977 = { + { + print 4008u16; + Uncle122 { + turn126: 18446744072884303255i32; + distribution123: 18446744073709551591i8; + station124: committee2302; + fan129: price2316; + snow125: rain2773; + orange127: ducks2933; + run128: 22303i16; + coal130: help793; + }.turn126; + Answer1090 { + sponge64: 1245382907745191222i64; + song57: anger596; + breath56: comparison1889; + stamp58: 14612633784994815847u64; + scarf59: hope1122; + trade63: void(); + downtown65: ground2502; + change60: yoke1022; + grip61: 18446744073709533308i16; + lamp62: business2306; + nerve55: sneeze830; + }.lamp62; + void() + }; + (1592678456u32 + balls2226) +}; +function throne82(unit2954: i32,river2955: u16) -> void hole2956 = print hen2976 = sheet2977; +function sticks2951(cactus2952: void,nation2953: i32) -> North85 farmer1397; +function apples113(ticket2943: u32,apple2944: u8,water2945: u8,disease2946: u32,twig2947: i32,process2948: i64,collar2949: u16,crate2950: Cable92) -> North85 sticks2951(business2306,relation2225); +function boat139(list2942: i16) -> Cable92 business2306; +struct Uncle122 { + turn126; + distribution123: i8; + station124; + fan129; + snow125: i8; + orange127: i16; + run128; + coal130: void; +} +struct Sand141 { + sister54: struct {scarf59: i16; + grip61: i16; + change60: _; + song57: u64; + stamp58: _; + nerve55: i16; + lamp62: _; + trade63: void; + sponge64: _; + downtown65: u16; + breath56: _;}; + cart68; + van53: u16; + guitar66; + ghost67: i64; +} +function desire190(digestion2941: u64) -> u32 (art1743 - { + 1316u16; + business2306; + lock1173; + 53350u16 +}); +function rabbit2928(space2940: i16) -> void void(); +function bed2935(loss2938: i16,road2939: void) -> Brush2521 { + pigs1701; + babies2143; + babies2143 +}; +jeans2931 = bed2935(clock2936 = ducks2937 = hearing2118.army1365,void()); +lead2927 = Answer1090 { + sponge64: Giraffe251 { + cart255: toad917; + coach253: side2498; + company252: clock2303; + pigs256: 1955684328000941704i64; + meeting254: 1485774205i32; + thread257: ground2502; + boys258: Ground237 { + baby241: board2646; + fly247: rock1004; + pin244: 5816391981068849226u64; + join239: 2974358622u32; + joke243: 4709258511929632351u64; + lace240: rain2773; + art245: weather1645; + quartz238: cub1979; + banana242: 15643539869381563002i64; + achieve246: 5151405922214683691i64; + hands248: anger596; + quilt249: measure2648; + transport250: Uncle122 { + turn126: sail1994; + distribution123: plants2422; + station124: condition1744; + fan129: 13727945480453375915u64; + snow125: yoke1022; + orange127: 18446744073709541032i16; + run128: 1017i16; + coal130: lock1173; + }; + }; + }.pigs256; + song57: 15835663339299163436u64; + breath56: { + effect2599; + hearing2118.fiction1370; + void(); + rabbit2928(measure1991); + curtain2929 = cough2722 + }; + stamp58: team1741.umbrella317; + scarf59: 18446744073709532411i16; + trade63: { + void(); + receipt792; + { + cork2930 = Answer1090 { + sponge64: 11727694617518639374i64; + song57: rabbits2045; + breath56: 18446744073709551574i8; + stamp58: brass2040; + scarf59: 18446744073709519764i16; + trade63: business2306; + downtown65: finger1315; + change60: yoke1022; + grip61: board2043; + lamp62: void(); + nerve55: 18446744073709536997i16; + }; + void(); + void(); + Judge45 { + dust50: 18446744073709537169i16; + pleasure69: 2715991746u32; + babies48: 71u8; + icicle46: 18446744073709523684i16; + tray49: quiver2774; + flavor70: stew2232; + stew71: 18446744072365872007i32; + order47: 18446744073709551591i8; + flame51: eggs2035; + adjustment52: Sand141 { + sister54: cork2930; + cart68: birthday2535; + van53: mailbox2309; + guitar66: condition1744; + ghost67: 13109042969737903904i64; + }; + } + }; + print 18446744073709530913i16 + }; + downtown65: jeans2931.calendar2523; + change60: fact1172; + grip61: oil2932 = ducks2933 = 18446744073709549486i16; + lamp62: bomb2934 = void(); + nerve55: 18446744073709551503i8; +}; +function corn227(wall2925: i32) -> struct {sister54: struct {scarf59: i16; +grip61: i16; +change60: _; +song57: u64; +stamp58: _; +nerve55: i16; +lamp62: _; +trade63: void; +sponge64: _; +downtown65: u16; +breath56: _;}; +cart68: i16; +van53: u16; +guitar66: u32; +ghost67: _;} sidewalk2926 = Sand141 { + sister54: lead2927; + cart68: uncle2009; + van53: measure1804.spy1797; + guitar66: tax2022.building1776; + ghost67: 6078671098525828208i64; +}; +function journey224(moon2923: i16,question2924: u16) -> Uncle122 wrist2870; +function language217(blade2919: u64,stone2920: i32,show2921: u16) -> i8 hose2922 = 18446744073709551524i8; +function dolls222(motion2917: u16,join2918: u64) -> i32 cough2722; +function women229(card2912: i64,bomb2913: i16,north2914: u8,detail2915: u16,lace2916: u32) -> North85 { + lock1173; + business2306; + Answer1090 { + sponge64: 14379944972611448220i64; + song57: hot868; + breath56: 18446744073709551539i8; + stamp58: 3433937328302249012u64; + scarf59: 18446744073709542324i16; + trade63: void(); + downtown65: 25690u16; + change60: 7i8; + grip61: clock2303; + lamp62: void(); + nerve55: eggs2035; + }.lamp62 +}.reason1073; +education2910 = { + heat2911 = price2316; + 51100u16 +}; +function hate205(tank2909: i32) -> i32 Furniture1362 { + cent1369: 18446744073709537528i16; + arm1366: board2646; + spark1363: rock1004; + fiction1370: balls1437.curve462; + sound1373: Furniture1362 { + cent1369: clock2303; + arm1366: 18970u16; + spark1363: leg1497; + fiction1370: 41781u16; + sound1373: Connection750 { + surprise758: 10194585737513788663i64; + price756: button2241; + skirt759: 15415279963269786095i64; + giraffe763: finger2504; + punishment765: finger1315; + leg757: 2026549190i32; + play762: 12957347647800944807u64; + son766: quiver2120; + baby761: measure2648; + berry751: education2910; + vegetable760: 118u8; + vase764: 62352u16; + }; + beam1371: 250u8; + army1365: 27415i16; + joke1372: smile1691; + carpenter1368: committee2302; + clam1367: birthday2535; + hall1364: 823469610i32; + }.sound1373; + beam1371: jam2111.battle1473; + army1365: Answer1090 { + sponge64: wall2007; + song57: price2316; + breath56: 24i8; + stamp58: slave2214; + scarf59: 24523i16; + trade63: void(); + downtown65: 1299u16; + change60: stew2232; + grip61: 18446744073709532545i16; + lamp62: void(); + nerve55: 15127i16; + }.grip61; + joke1372: (things1644 * payment2037); + carpenter1368: { + cause2079; + void(); + void(); + 18446744073709551603i8; + writer1399 + }; + clam1367: yoke1022; + hall1364: potato2616; +}.hall1364; +function exchange233(shirt2908: u16) -> i32 1379264213i32; +struct butter2901 { + mice23; + grade27: u8; + surprise17: u16; + stranger25: i64; + channel21: u64; + camp18: i16; + love19; + letter26; + pickle20: u64; + territory16: i16; + action22: u16; + fold24: i64; +} +function parcel2905(night2906: u32) -> Answer1090 sail2907 = { + void(); + () +}; +function war234(mom2899: i16,love2900: u16) -> struct {territory16: _; +camp18: i16; +action22: u16; +grade27: u8; +letter26: u32; +surprise17: _; +fold24: i64; +mice23: _; +stranger25: i64; +pickle20: u64; +channel21: _; +love19: _;} butter2901 { + mice23: { + void(); + slave2214 + }; + grade27: yak2902 = (lunch1696 / bells1703); + surprise17: { + plane2039; + 13946524961670247109i64 + }.crow316; + stranger25: lock1173; + channel21: { + pot2903 = pocket2705; + pencil2144.geese693; + burn1828.songs1591; + 16133642598560116912i64; + { + birthday2535; + 7962729541973914765u64 + }; + void() + }; + camp18: { + 94i8; + Fork1583 { + mist1584: lock1173; + trees1585: 11648073170823199670i64; + servant1586: pencil2144; + }.mist1584; + 29594i16; + { + void(); + void(); + business2306; + pigs1701; + 36i8 + }; + 1446057900u32 + }; + love19: salt1925.driving1794.cattle1781; + letter26: zipper2904 = 3214885253u32; + pickle20: plane2039; + territory16: parcel2905(2247094546u32).grip61; + action22: { + lock1173; + 3134638775637279428i64; + 20219u16 + }.baby241; + fold24: 6707968298235526599i64; +}; +struct Ground237 { + baby241: u16; + fly247; + pin244; + join239: u32; + joke243: u64; + lace240: i8; + art245: u16; + quartz238: i32; + banana242; + achieve246; + hands248: u64; + quilt249: i64; + transport250: Uncle122; +} +struct Giraffe251 { + cart255: i16; + coach253: i32; + company252: i16; + pigs256; + meeting254: i32; + thread257: u16; + boys258: Ground237; +} +struct number195 { + father9: u64; + fight15: struct {territory16: _; + camp18: i16; + action22: u16; + grade27: u8; + letter26: u32; + surprise17: _; + fold24: i64; + mice23: _; + stranger25: i64; + pickle20: u64; + channel21: _; + love19: _;}; + size10: i16; + poison2; + pickle12: u8; + watch7: i32; + trip3: u64; + disease4: i32; + crown13: i64; + direction11: i64; + drop14: u16; + earthquake8: u8; + pizzas6: u32; + twist5; + temper28: i64; +} +function balls264(wool2898: i16) -> void hope1839.mist1584; +function fireman261(afterthought2895: u8,daughter2896: u64) -> Uncle122 { + void(); + weather1645; + heat2897 = lock1173 +}; +function talk2891(frogs2893: u16,fall2894: u64) -> Answer1090 ().flight1295; +function news2889(loss2890: u64) -> Answer1090 talk2891(driving2892 = trucks2702,{ + void(); + { + void(); + 9637399037841773545i64; + void() + } +}); +twig2887 = Deer969 { + calendar966: 18446744073709551537i8; + birthday964: { + salt1925.holiday1793; + Bath2102 { + measure2103: woman1614; + rake2104: 18446744073709530221i16; + lake2105: Screw1951 { + spade1956: banana2307; + basin1952: Song1795 { + popcorn1796: Tree348 { + vessel349: weather1645; + stage350: 13162546163719015427u64; + worm351: 17034u16; + chalk352: glue2229; + robin353: basket914; + }; + news1800: salt1925; + spy1797: 2310u16; + joke1799: 10174914971450260108u64; + owl1798: 252u8; + }; + cork1959: Profit1891 { + governor1894: group2753; + insurance1892: 8868676392271170733i64; + surprise1893: 45758u16; + glass1895: 18446744071727018341i32; + sign1896: Banana1053 { + story1063: cord2030; + amusement1055: canvas433; + wrench1062: 18446744072484945478i32; + boats1071: button2241; + action1072: 18446744073709518971i16; + reason1073: North85 { + day87: quiver1858; + brake86: 1998i16; + apple88: 42231u16; + }; + vessel1069: 6823501294085668620u64; + driving1060: 241u8; + leather1061: 18446744073709542693i16; + waves1070: 18446744073709551584i8; + visitor1056: 590439173i32; + committee1054: 18446744072808271389i32; + locket1059: 6177361260152179352u64; + cast1058: slave2214; + tank1057: 18446744073709535045i16; + }; + }; + sun1954: grade1619; + reading1955: 6148365270883662047i64; + card1953: partner2767; + wood1957: 16223205517614949195i64; + page1958: 5526967166850136007i64; + }; + }; + Uncle122 { + turn126: cub1979; + distribution123: 18446744073709551558i8; + station124: 177078161u32; + fan129: hot868; + snow125: 66i8; + orange127: uncle2009; + run128: cough2722; + coal130: void(); + }.coal130; + blood2888 = woman2645; + news2889(hot868).lamp62; + 48u8 + }; + pancake968: Cable92 { + patch97: relation2225; + lock98: plants2422; + popcorn93: 3857095090662677087i64; + hydrant95: soup2500; + summer94: 121u8; + coast99: bead2619; + cord96: 6639i16; + }.coast99.stew71; + wind967: lift2242; + direction965: { + void(); + help793; + 18446744072067614120i32; + void(); + void(); + tendency908; + rock1004; + business2306; + 30713i16 + }.apparatus1155.calendar966; +}; +pets2859 = twig2887; +function regret2878(mind2882: i16,rat2883: u32,apple2884: u16,visitor2885: u64,bubble2886: i32) -> i64 8255042272091070022i64; +function crowd2872(polish2877: i16) -> Fork1583 Fork1583 { + mist1584: print 501355974i32; + trees1585: regret2878(thrill2879 = quiver1175,trucks2880 = goldfish856,{ + uncle2009; + woman1614; + void(); + receipt792; + trucks2702 + },tent2881 = banana2307,yoke1280); + servant1586: pencil2144; +}; +function kiss2873(stove2875: u64,growth2876: i8) -> i64 example2597; +function tent2863() -> i8 { + 27477u16; + woman2645; + crowd2872(22891i16).trees1585; + { + kiss2873(slave2214,yoke1022); + void(); + poison2874 = 127u8; + slave2214; + void(); + (18446744073582609635i32 * 3227033i32) + }; + Control1590 { + songs1591: void(); + development1593: void(); + winter1594: dress1612; + taste1595: Fork1583 { + mist1584: lock1173; + trees1585: woman2506; + servant1586: pencil2144; + }; + grain1592: 124i8; + }.taste1595.mist1584; + void() +}; +apples2869 = wrist2870 = { + 1998564260670650i64; + cover2871 = receipt792; + print 8377861834183099026i64 +}; +function skin2862(songs2864: u16,pets2865: i8,gold2866: u64) -> Father456 shoe2867 = wilderness2868 = Father456 { + whip458: Giraffe251 { + cart255: hope1122; + coach253: 18446744073402389667i32; + company252: cough2722; + pigs256: wall2007; + meeting254: 18446744072721433349i32; + thread257: 44006u16; + boys258: Ground237 { + baby241: 56545u16; + fly247: balls2226; + pin244: 4219438199145160328u64; + join239: 4255177018u32; + joke243: hot868; + lace240: yoke1022; + art245: 60509u16; + quartz238: 801747133i32; + banana242: 17345417051265114085i64; + achieve246: 9378084796658486257i64; + hands248: plane2039; + quilt249: increase1607; + transport250: apples2869; + }; + }; + fifth457: rabbits2045; +}; +function sock2858(bears2860: u64,shape2861: u32) -> Father456 skin2862(61289u16,tent2863(),1201437151960906967u64); +chess2857 = sock2858(Holiday2012 { + cook2016: Spark1147 { + fruit1153: bells1095; + muscle1149: price2316; + church1151: process1265; + hate1154: 4245649006u32; + apparatus1155: pets2859; + sneeze1148: 1667052408922670261i64; + feather1152: 2448i16; + rake1150: bells1095; + }; + bike2013: request2533; + drum2014: 9832148498900309756u64; + feet2015: twig1739; +}.drum2014,Ground237 { + baby241: 17359u16; + fly247: 2567523890u32; + pin244: anger596; + join239: rock1004; + joke243: 12043825672126467824u64; + lace240: book2772; + art245: 33142u16; + quartz238: grade1640; + banana242: woman2506; + achieve246: bulb2290; + hands248: anger596; + quilt249: example2597; + transport250: Uncle122 { + turn126: 2038143019i32; + distribution123: coal1020; + station124: leg1497; + fan129: 2980252475220983714u64; + snow125: brass1421; + orange127: cough2722; + run128: clock2303; + coal130: void(); + }; +}.join239); +function creature275(tent2856: u16) -> Giraffe251 chess2857.whip458; +function anger271(able2853: u64) -> Uncle122 tub2854 = Uncle122 { + turn126: 488621910i32; + distribution123: bead2619.order47; + station124: stick1983.giraffe763; + fan129: adjustment2855 = hydrant2526; + snow125: Friction1287 { + page1289: 18446744073709551602i8; + fight1294: swing2292; + crayon1291: 412072673i32; + color1288: 2299141670075546434i64; + hate1293: 20929u16; + observation1290: 201u8; + flight1295: distribution1709; + rabbit1292: 34u8; + }.page1289; + orange127: 27859i16; + run128: cough2722; + coal130: void(); +}; +struct credit168 { + grip61; + sponge64; + breath56; + scarf59: i16; + song57: u64; + stamp58; + lamp62; + change60: i8; + downtown65: u16; + trade63: void; + nerve55: i16; +} +function day282(snail2850: u16,patch2851: u16,oven2852: u64) -> i8 34i8; +struct transport2842 { + dock2822; + shoe2825: u16; + pan2828: u64; + toys2820: u32; + roof2826: u16; + bun2827; + yam2823: i16; + guide2824: i32; + suit2821; +} +function pig2843(ear2848: i32,steel2849: u64) -> u16 20646u16; +function chair2845(prison2846: void,things2847: u16) -> i32 wren2497; +mom2831 = transport2842 { + dock2822: ({ + (12910i16 + 18446744073709524789i16); + business2306; + void(); + woman2645 + } * condition1744); + shoe2825: pig2843(lift2242,4383574975377663122u64); + pan2828: print { + 21479i16; + business2306 + }; + toys2820: goat2844 = cemetery2704; + roof2826: art1743; + bun2827: clock2303; + yam2823: 18446744073709533708i16; + guide2824: 18446744072001571146i32; + suit2821: chair2845(void(),woman2645); +}; +function seed2839(theory2840: i16,badge2841: i8) -> void print vegetable2031.milk1904; +function brick2819(government2834: struct {suit2821: i32; +roof2826: u16; +toys2820: u32; +yam2823: i16; +guide2824: _; +shoe2825: u16; +dock2822: u32; +bun2827: i16; +pan2828: u64;},crowd2835: i8,hat2836: u8,chin2837: u32) -> Uncle122 milk2838 = Uncle122 { + turn126: { + 67i8; + 5100u16 + }; + distribution123: 18446744073709551558i8; + station124: 18379u16; + fan129: brass2040; + snow125: 18446744073709551523i8; + orange127: Test1472 { + plant1476: Note1190 { + unit1067: 18446744073709551558i8; + bike1066: plane2039; + food1068: hope1122; + burst1065: 239u8; + coil1064: 14465825009102049157i64; + }; + battle1473: 16u8; + rainstorm1474: smell1860; + temper1475: 18446744073709522912i16; + }.temper1475; + run128: 18446744073709551605i8; + coal130: seed2839(birthday2535,18446744073709551541i8); +}; +function smile281(juice2816: i16,prison2817: i16,steel2818: i32) -> Uncle122 brick2819(sand2829 = arithmetic2830 = mom2831,rice2832 = 40i8,stranger2833 = (loss1695 * 246u8),slave2299); +struct money284 { + grip61: i16; + downtown65: u16; + breath56: Judge45; + sponge64: u16; + stamp58; + trade63: void; + change60: i64; + nerve55: i16; + lamp62; + scarf59: i16; + song57; +} +function belief2810(society2813: i32,oranges2814: i8) -> i8 { + print clock2815 = request2533; + 8758i16; + { + 58147u16; + { + waves2615; + void(); + void(); + receipt792; + 54244u16 + }; + prison882.vessel349; + 45954u16 + } +}; +boats2808 = { + (brain2809 = (number2421 * 62i8) + brass1421); + print belief2810({ + 314737886u32; + month2289; + woman2645; + 249u8; + help793 + },whip2811 = 105i8); + { + business2306; + machine2812 = void(); + print { + 764326145263469928u64; + swing2292; + quiver2774 + }; + { + { + woman2645; + void() + }; + (price2316 + button2241); + 11766977564394004085i64 + }; + void(); + lock1173 + }; + 4494u16 +}; +function knot158(flesh2807: i32) -> Sand141 Sand141 { + sister54: boats2808; + cart68: Servant2377 { + work2378: 4516276047523771887i64; + print2381: Bells2362 { + pan2363: pocket2705; + knee2364: prison2492; + }; + paper2380: 18446744073709536992i16; + grain2379: hammer1349; + }.paper2380; + van53: (); + guitar66: 52460u16; + ghost67: thumb888; +}; +function pail290(soda2801: u64,rake2802: i16,polish2803: u16,cap2804: u8) -> Giraffe251 { + 115i8; + experience2805 = writer1399; + maid2806 = sticks2089 +}; +function heart301(comb2795: i8,cobweb2796: i16,net2797: i16,hobbies2798: i64) -> u64 skin2799 = army2800 = price2316; +struct anger302 { + grip61: i16; + scarf59: i16; + sponge64: u64; + song57: u64; + breath56; + stamp58; + change60: i64; + trade63: void; + nerve55: i16; + lamp62; + downtown65: u16; +} +function way299(tent2791: u32,giants2792: i64,beef2793: u16) -> void { + help793; + 610592028u32; + pizzas2794 = mitten2046 +}; +function observation2784(fear2787: u16,mouth2788: i8,bit2789: i32,bell2790: u32) -> i8 waves2615; +function pain308() -> Sand141 Sand141 { + sister54: Answer1090 { + sponge64: { + help793; + void(); + wire2308; + relation2225 + }; + song57: way2740.connection2735; + breath56: observation2784(pet1316,wrist790,18446744072232352931i32,cemetery2704); + stamp58: beast2785 = tendency908; + scarf59: eggs2035; + trade63: business2306; + downtown65: 56u8; + change60: 50i8; + grip61: blow2786 = 18922i16; + lamp62: lock1173; + nerve55: (18446744073709531306i16 - straw1740); + }; + cart68: 29598i16; + van53: canvas433; + guitar66: fear1395.summer94; + ghost67: 13726270095397291202i64; +}; +struct Band315 { + answer322; + crow316: u16; + umbrella317: u64; + summer321: i64; + substance319; + earthquake320: i16; + exchange318: i16; +} +function neck328(chickens2782: u64,waves2783: i32) -> u16 2065u16; +function throat398(stomach2776: i64,stomach2777: i64,existence2778: u16,alarm2779: i32,veil2780: u16) -> u64 brain2781 = 17399137964502923994u64; +function system400(quiver2775: u32) -> i64 13160352778158542500i64; +star406 = (); +oil407 = 238u8; +smoke408 = 18446744073709522399i16; +children409 = quiver2774 = bath2050; +pan410 = book2772 = rain2773 = 73i8; +dime411 = 18446744073709551550i8; +bedroom412 = (writer1399 + board2043); +function decision2720(cast2771: u64) -> u8 eyes1242; +popcorn2760 = void(); +function vessel2763(story2768: i8,apple2769: i8,coil2770: void) -> void print { + coil2770; + void() +}; +spade2762 = Friction1287 { + page1289: { + vessel2763(Answer1090 { + sponge64: 9731758591651739755i64; + song57: hot868; + breath56: fact1172; + stamp58: 3532036174801180537u64; + scarf59: 18446744073709526584i16; + trade63: lock1173; + downtown65: 37659u16; + change60: hammer1349; + grip61: maid1746; + lamp62: void(); + nerve55: 18446744073709534135i16; + }.breath56,plants2422,form2764 = business2306); + thing2765 = toe2766 = help793 + }; + fight1294: 5814033165166282021i64; + crayon1291: partner2767 = 1815642865i32; + color1288: bulb2290; + hate1293: Sand141 { + sister54: basket914.sister54; + cart68: pets890; + van53: 87u8; + guitar66: 560418797u32; + ghost67: comparison1889; + }.van53; + observation1290: (248u8 + smile1691); + flight1295: { + void(); + Fork1583 { + mist1584: woman2645; + trees1585: jar2714; + servant1586: pencil2144; + }.mist1584 + }; + rabbit1292: 103u8; +}; +sand2761 = spade2762; +spoon2721 = Expert1681 { + payment1682: 4492967156445107735u64; + fiction1686: { + 3987260454u32; + 9u8 + }; + camp1683: popcorn2760.sofa2675; + brothers1684: mitten2046; + leather1685: 2570263413384063984u64; + rail1687: sand2761; +}; +band2752 = Answer1090 { + sponge64: group2753 = (); + song57: 12413781481149238458u64; + breath56: { + front2754 = void(); + void() + }; + stamp58: rabbits2045; + scarf59: (news2755 = Cloud2734 { + sail2737: 115i8; + street2736: 2998054885u32; + roll2738: 24389i16; + connection2735: 2680040304287021190u64; + design2739: Quiet2729 { + wind2732: vegetable2031; + rub2730: side2498; + waste2731: sneeze830; + }; + }.roll2738 / crayon2756 = cough2722); + trade63: print 11802488760473179901i64; + downtown65: woman2645; + change60: { + receipt792; + { + void(); + receipt792 + }; + 18413074048268191293u64; + morning2757 = 3731598204u32 + }; + grip61: { + 18446744073709551550i8; + peace2758 = void(); + credit2759 = 12158i16; + 18446744073709525533i16; + void() + }; + lamp62: print { + woman2645; + void() + }; + nerve55: (uncle2009 - { + 4232020374u32; + void(); + soup2500; + 18446744073709551583i8; + 18446744073709528549i16; + void(); + 14399545735477813923i64; + help793 + }.cent1369); +}; +ducks2724 = band2752; +function voyage2726(bait2751: void) -> u32 (cemetery2704 + porter1927.furniture754); +struct Quiet2729 { + wind2732: Animal1913; + rub2730: i32; + waste2731: i16; +} +struct Cloud2734 { + sail2737: i8; + street2736: u32; + roll2738: i16; + connection2735: u64; + design2739: Quiet2729; +} +struct cork2750 { + milk2747: i64; + quiver2741: i16; + throat2743: u16; + gate2748; + cake2749: u16; + soup2746: u64; + decision2745: i64; + nest2744; + health2742; +} +sugar2733 = way2740 = { + cork2750 { + milk2747: amusement887; + quiver2741: 21199i16; + throat2743: 10019u16; + gate2748: planes2703; + cake2749: 32844u16; + soup2746: 8649969366547264322u64; + decision2745: example2597; + nest2744: weather1645; + health2742: 5530898473182904875i64; + }; + smile1691 +}.design2739; +function cable2725(fall2727: u32) -> Skin1115 Skin1115 { + mother1121: punishment2728 = Cats720 { + cannon724: side2498; + street721: 12408916272683724982i64; + fold725: prison882; + chance722: fall2727; + cave723: cemetery2704; + }; + tax1117: Song1795 { + popcorn1796: prison882; + news1800: salt1925; + spy1797: 54448u16; + joke1799: button2241; + owl1798: 129u8; + }.popcorn1796; + oil1118: 204u8; + company1120: sugar2733.wind2732.example1900; + bubble1116: pin2496; + badge1119: { + 17620712480829846294u64; + { + dress1612; + increase1607; + business2306; + spring2534 + }; + 33923u16 + }; +}; +meal413 = Sand141 { + sister54: Answer1090 { + sponge64: decision2720(11222072116547751408u64); + song57: 16855640595290019178u64; + breath56: 21i8; + stamp58: spoon2721.fiction1686; + scarf59: 18446744073709533354i16; + trade63: void(); + downtown65: 36338u16; + change60: 18446744073709551589i8; + grip61: cough2722 = waves2615; + lamp62: angle2723 = Control1590 { + songs1591: woman2645; + development1593: void(); + winter1594: 53001u16; + taste1595: Fork1583 { + mist1584: void(); + trees1585: 752545740277942i64; + servant1586: Driving692 { + boot697: 10755758441182295583i64; + cub695: hammer1349; + stop694: anger596; + geese693: 12380u16; + pie698: 979496166668070144i64; + action699: sail1994; + box696: art1743; + plants700: Cable92 { + patch97: 18446744072999833112i32; + lock98: number2421; + popcorn93: bulb2290; + hydrant95: babies2143; + summer94: 153u8; + coast99: Judge45 { + dust50: 3188i16; + pleasure69: 3087433961u32; + babies48: 185u8; + icicle46: tiger2080; + tray49: 29u8; + flavor70: 21i8; + stew71: 1141437908i32; + order47: 62i8; + flame51: experience1094; + adjustment52: Sand141 { + sister54: ducks2724; + cart68: experience1094; + van53: 11411u16; + guitar66: 3140243369u32; + ghost67: chairs1873; + }; + }; + cord96: writer1399; + }; + }; + }; + grain1592: wrist790; + }.development1593; + nerve55: 18446744073709523207i16; + }; + cart68: 18446744073709546586i16; + van53: cable2725(voyage2726(business2306)).company1120; + guitar66: finger2504; + ghost67: 4839350695623833083i64; +}; +question414 = Skin1115 { + mother1121: Cats720 { + cannon724: side2498; + street721: Friction1287 { + page1289: 98i8; + fight1294: 11243779773243214943i64; + crayon1291: grade1640; + color1288: soup2500; + hate1293: 65176u16; + observation1290: 223u8; + flight1295: Answer1090 { + sponge64: 18265329452575439163i64; + song57: rabbits2045; + breath56: 18446744073709551601i8; + stamp58: 10766273565694140429u64; + scarf59: tiger2080; + trade63: void(); + downtown65: 16268u16; + change60: arch2416; + grip61: 14256i16; + lamp62: lock1173; + nerve55: 18199i16; + }; + rabbit1292: 191u8; + }.color1288; + fold725: amusement1803.popcorn1796; + chance722: { + 44040u16; + 18446744073709540846i16; + 2899907657u32 + }; + cave723: 3944027259u32; + }; + tax1117: { + 23198u16; + business2306 + }; + oil1118: Note1190 { + unit1067: waves2615; + bike1066: 12730009938513849771u64; + food1068: account1174; + burst1065: smash1096; + coil1064: start1419; + }.burst1065; + company1120: { + { + business2306; + void(); + ground2502; + void() + }; + 35522u16; + anger596; + dock2718 = finger2504; + Control1590 { + songs1591: void(); + development1593: void(); + winter1594: 421u16; + taste1595: Fork1583 { + mist1584: void(); + trees1585: 1317933619607938007i64; + servant1586: pencil2144; + }; + grain1592: 18446744073709551568i8; + }.development1593; + 18446744073709521354i16 + }; + bubble1116: (250u8 / waves2615); + badge1119: death2719 = 118u8; +}.bubble1116; +roof415 = 613367566u32; +sticks416 = cemetery2704; +girl417 = { + void(); + print goldfish856 +}; +function skate2715(page2716: i64,board2717: i16) -> void lock1173; +brothers418 = jar2714 = { + skate2715(5523637067206285191i64,24537i16); + 18446744073709540459i16; + riddle1984.lamp62 +}.egg1773; +function tent2708(error2710: i64,test2711: u8,temper2712: u64,berry2713: i8) -> Peace2389 son2706; +van2707 = tent2708(7405728892775373319i64,loss1695,12073554946513323993u64,teeth2709 = 18446744073709551572i8).start2398; +seashore419 = (example2597 + son2706 = Peace2389 { + frogs2397: plane2039; + curve2390: trade2008; + rice2393: 18446744073709551599i8; + start2398: van2707; + quicksand2394: 23609u16; + rain2391: 7964148107402594169i64; + cactus2395: 18446744073709540347i16; + button2392: board2043; + brass2396: 5715778880066874215i64; +}.curve2390); +jelly420 = comparison1889; +camp421 = pocket2705 = 146u8; +lead422 = void(); +drawer423 = Driving692 { + boot697: 14529369755203922185i64; + cub695: 18446744073709551595i8; + stop694: rabbits2045; + geese693: 17331u16; + pie698: 15854761984027339191i64; + action699: lift2242; + box696: 38321u16; + plants700: fear1395; +}.pie698.coal130; +prison2698 = Bag1767 { + death1769: (trucks2702 = secretary1857 + 32355u16); + quicksand1770: Band315 { + answer322: Shame2666 { + development310: planes2703 = plants2422; + observation311: 253u8; + calculator312: 14001634656381221885u64; + crime314: Hospital2516 { + fire2518: board2646; + house2519: 17628472218198637765u64; + turn2517: slave2214; + name2520: credit2622; + }.house2519; + scissors313: { + 3869351791u32; + 1450517756i32; + cub1979 + }; + }; + crow316: 55035u16; + umbrella317: price2316; + summer321: amusement887; + substance319: (legs1809.meat704 + cemetery2704 = balls2226); + earthquake320: 18651i16; + exchange318: 20536i16; + }; + death1768: 17322911887618844375i64; +}; +function apparatus2699() -> i16 cause2079; +function quilt2695(sister2696: u32,title2697: u8) -> Detail1771 Detail1771 { + alley1783: prison2698; + building1776: (pigs1701 / 17463u16); + space1775: 11777i16; + deer1777: Giraffe251 { + cart255: hope1122; + coach253: glue2229; + company252: account1174; + pigs256: 14564030367372698208i64; + meeting254: potato2616; + thread257: mailbox2309; + boys258: reading2300; + }.boys258.achieve246; + ants1780: apparatus2699(); + cattle1781: cellar919; + trucks1772: chalk2700 = 20908i16; + government1779: 32827u16; + acoustics1782: 3i8; + egg1773: 1085951689252985860i64; + growth1778: kitty2701 = 13897908230446672653u64; + use1774: { + pin2496; + void(); + 17953008857158807418u64 + }; +}; +bat424 = quilt2695(4022859413u32,bells1703).acoustics1782; +impulse425 = { + { + void(); + tendency908 + }.apple88; + { + woman2645; + Uncle122 { + turn126: wren2497; + distribution123: waves2615; + station124: goldfish856; + fan129: 12427986735125296275u64; + snow125: 18446744073709551516i8; + orange127: 26666i16; + run128: cause2079; + coal130: receipt792; + }.coal130; + { + lock1173; + void() + } + }; + { + cow2694 = (ground2502 / 18367u16); + (14806506861626546334i64 * Shame2666 { + development310: 18446744073709551541i8; + observation311: 195u8; + calculator312: 10894812196888899772u64; + crime314: 7363187623901162834u64; + scissors313: 8469340786408687888i64; + }.scissors313) + }; + print { + 226u8; + hydrant2526 + }.rake1150 +}; +afternoon442 = stocking2693 = loss1695; +reading443 = banana2307; +blow444 = nail832; +body445 = { + 24427i16; + brother834 +}; +cabbage2690 = Father456 { + whip458: disgust2691 = care2692 = edge521; + fifth457: 1283555874078136244u64; +}; +sock446 = cabbage2690.whip458.boys258.transport250; +struct Clam2667 { + cows2672: Shame2666; + secretary2670: u64; + laborer2669; + cream2668: i64; + coil2671: u16; +} +struct Father2674 { + sofa2675: u16; + chair2678: Clam2667; + boats2676: u16; + cobweb2677: u64; +} +function grandfather2686(example2687: i8) -> Control1590 boats2688 = Control1590 { + songs1591: receipt792; + development1593: void(); + winter1594: 18321u16; + taste1595: drug2689 = Fork1583 { + mist1584: void(); + trees1585: breakfast889; + servant1586: Driving692 { + boot697: meal2291; + cub695: arch2416; + stop694: business2527; + geese693: 40315u16; + pie698: surprise2041; + action699: 1737612345i32; + box696: canvas433; + plants700: Cable92 { + patch97: process1265; + lock98: example2687; + popcorn93: 6178706911965966747i64; + hydrant95: 8639210084009229435i64; + summer94: 209u8; + coast99: person2618; + cord96: toothpaste2211; + }; + }; + }; + grain1592: brass1421; +}; +function sky2680(hope2681: i64,dock2682: u64,snow2683: u16) -> Control1590 property2684 = cent2685 = grandfather2686(arch2416); +winter2673 = { + void(); + { + lock1173; + lock1173 + }; + fire2679 = 18446744073709543579i16; + print 404596653i32; + sky2680(13041905791709896483i64,7428551166177077742u64,dress1612).development1593 +}.chair2678; +struct Shame2666 { + development310: i8; + observation311; + calculator312; + crime314: u64; + scissors313; +} +function quill2662(trucks2664: i64,oranges2665: i64) -> struct {observation311: u8; +development310: i8; +calculator312: u64; +crime314: _; +scissors313: i64;} winter2673.cows2672; +function key2654(tongue2661: u8) -> struct {observation311: u8; +development310: i8; +calculator312: u64; +crime314: _; +scissors313: i64;} quill2662(tiger2663 = woman2645,1075390948586768013i64); +function blood2657(growth2658: i8) -> Deer969 Deer969 { + calendar966: berry2659 = void(); + birthday964: cabbage2660 = board2646; + pancake968: (); + wind967: 620880475i32; + direction965: request2533; +}; +function lip501(feeling2653: i64) -> Band315 Band315 { + answer322: key2654((attack2125 / 105u8)); + crow316: { + bean2656 = blood2657(camp2212); + sand2655 = 8203691653435205150i64; + bean2656.birthday964 + }; + umbrella317: Connection750 { + surprise758: soup2500; + price756: 3686288172545785679u64; + skirt759: trade2008; + giraffe763: 2113795605u32; + punishment765: secretary1857; + leg757: 1431880192i32; + play762: button2241; + son766: Yoke743 { + school746: Fowl461 { + curve462: spring2534; + existence463: 18446744073709544890i16; + feet464: number2421; + leg466: 27439u16; + clocks467: Father456 { + whip458: play894; + fifth457: 3467661636496165120u64; + }; + net465: pet1316; + }; + pail744: 1834419529u32; + show745: rabbits2045; + }; + baby761: 1058907909715730189i64; + berry751: Doll1647 { + truck753: 11116427951800886902u64; + furniture754: condition1744; + watch752: 18446744072837751706i32; + walk755: 9978096388877199811i64; + }; + vegetable760: loss1695; + vase764: 19542u16; + }.price756; + summer321: copy1689; + substance319: lunch1696; + earthquake320: 21114i16; + exchange318: 18446744073709535001i16; +}; +struct Angle2635 { + women2642: Eye2586; + fireman2639: i16; + road2637: struct {popcorn2638: _;}; + relation2640: u8; + heat2641: u64; + battle2636: i16; +} +fog2643 = { + 1947550441984751245u64; + board2646 +}; +struct order2652 { + popcorn2638: i8; +} +drawer2644 = order2652 { + popcorn2638: number2421; +}; +function bucket2647(appliance2649: i64,servant2650: u16,addition2651: u32) -> void void(); +shade2634 = Answer1090 { + sponge64: (chairs1873 / { + 15077994059984074346i64; + (start1419 + clouds1420); + 14042545467376776950i64 + }); + song57: Angle2635 { + women2642: Eye2586 { + pin2588: 15069u16; + circle2590: fog2643; + stage2589: 169u8; + airplane2587: slave2214; + }; + fireman2639: 19641i16; + road2637: drawer2644; + relation2640: 207u8; + heat2641: 10040252875717371694u64; + battle2636: maid1746; + }.women2642.airplane2587; + breath56: plants2422; + stamp58: 999670682u32; + scarf59: twig1739; + trade63: woman2645 = print loss1695; + downtown65: board2646 = 13367u16; + change60: 18446744073709551562i8; + grip61: 11019i16; + lamp62: bucket2647(measure2648 = 8774864860877359687i64,secretary1857,2347874237u32); + nerve55: 18446744073709534756i16; +}; +function spiders549(dad2630: u16,end2631: u64) -> Judge45 Judge45 { + dust50: 18446744073709539193i16; + pleasure69: { + { + business2306; + 18446744073709534765i16 + }; + void() + }; + babies48: 12u8; + icicle46: 20296i16; + tray49: 46u8; + flavor70: 110i8; + stew71: (dad2630 / (1358998751i32 - quiver1858)); + order47: representative2632 = 18446744073709551568i8; + flame51: vegetable2031.horse1910; + adjustment52: Sand141 { + sister54: edge2633 = shade2634; + cart68: brother834; + van53: weather1645; + guitar66: finger1315; + ghost67: 5805980066243214982i64; + }; +}; +function size551(needle2627: u16,nation2628: i64) -> struct {sister54: struct {scarf59: i16; +grip61: i16; +change60: _; +song57: u64; +stamp58: _; +nerve55: i16; +lamp62: _; +trade63: void; +sponge64: _; +downtown65: u16; +breath56: _;}; +cart68: i16; +van53: u16; +guitar66: u32; +ghost67: _;} reward2629 = Sand141 { + sister54: Answer1090 { + sponge64: 3668355524541796926i64; + song57: 7550474795978354762u64; + breath56: 115i8; + stamp58: price2316; + scarf59: writer1399; + trade63: void(); + downtown65: 63138u16; + change60: 18446744073709551575i8; + grip61: 18446744073709535881i16; + lamp62: lock1173; + nerve55: 13393i16; + }; + cart68: twig1739; + van53: dress1612; + guitar66: 1543839378u32; + ghost67: 6625768199783582391i64; +}; +function wax2623(scarf2624: u32,cactus2625: u16) -> Peace2389 flight2626 = credit2622; +cactus2621 = credit2622 = wax2623((62899399u32 * 1879344637u32),driving2293.surprise1893).start2398; +gun569 = Ground237 { + baby241: { + void(); + receipt792; + business2527 + }; + fly247: (1322646424u32 + 554854176u32); + pin244: committee2302; + join239: (3113305312u32 * 964280391u32); + joke243: 13724721626594292983u64; + lace240: Peace2389 { + frogs2397: rabbits2045; + curve2390: example2597; + rice2393: 20i8; + start2398: cactus2621; + quicksand2394: 3838u16; + rain2391: 11516781724428009806i64; + cactus2395: twig1739; + button2392: 18446744073709520673i16; + brass2396: start1419; + }.rice2393; + art245: Friction1287 { + page1289: yoke1022; + fight1294: 5133146612531603671i64; + crayon1291: yoke1280; + color1288: soup2500; + hate1293: 18839u16; + observation1290: 135u8; + flight1295: riddle1984; + rabbit1292: 176u8; + }.hate1293; + quartz238: Cub1830 { + plot1831: 18446744073251141803i32; + birth1832: Control1590 { + songs1591: void(); + development1593: void(); + winter1594: 22681u16; + taste1595: Fork1583 { + mist1584: lock1173; + trees1585: name2287; + servant1586: Driving692 { + boot697: 10393255146839375720i64; + cub695: waves2615; + stop694: button2241; + geese693: 34255u16; + pie698: 14799080070965781087i64; + action699: 1897202284i32; + box696: spring2534; + plants700: fear1395; + }; + }; + grain1592: 18446744073709551520i8; + }; + }.plot1831; + banana242: (12855810369199687482i64 / clouds1420); + achieve246: example2597; + hands248: price2316; + quilt249: experience1094; + transport250: Uncle122 { + turn126: potato2616; + distribution123: 70i8; + station124: condition1744; + fan129: rabbits2045; + snow125: 18446744073709551539i8; + orange127: effect2599; + run128: 18446744073709550834i16; + coal130: business2306; + }; +}.transport250.orange127; +furniture574 = 418665572u32; +beggar2620 = Driving692 { + boot697: 1875242633053620351i64; + cub695: waves2615; + stop694: 10180436434519956174u64; + geese693: 27585u16; + pie698: example2597; + action699: glue2229; + box696: 3879u16; + plants700: Cable92 { + patch97: process1265; + lock98: 118i8; + popcorn93: 3068226387635784701i64; + hydrant95: woman2506; + summer94: smile1691; + coast99: Judge45 { + dust50: 18446744073709523627i16; + pleasure69: 91227373u32; + babies48: 185u8; + icicle46: 2133i16; + tray49: 12u8; + flavor70: 115i8; + stew71: process1265; + order47: 18446744073709551511i8; + flame51: 18446744073709547368i16; + adjustment52: cats1547; + }; + cord96: maid1746; + }; +}.plants700.coast99; +dime2617 = person2618 = bead2619 = beggar2620; +boy575 = potato2616 = ({ + pigs1422; + help793; + art1743; + { + void(); + receipt792 + }; + 24370i16 +} * (18446744071664987309i32 * Cable92 { + patch97: 830670364i32; + lock98: 17i8; + popcorn93: cellar919; + hydrant95: 6614777576776724471i64; + summer94: 24u8; + coast99: dime2617; + cord96: 19760i16; +}.patch97)); +string576 = waves2615 = fact1172; +struct income577 { + stamp58: u16; + downtown65: u16; + lamp62; + trade63: void; + sponge64: u64; + breath56: i16; + scarf59: i16; + change60: i16; + grip61: i16; + song57: u64; + nerve55; +} +heart578 = void(); +home579 = 1014833411673363854u64; +struct Eye2586 { + pin2588; + circle2590: Servant2377; + stage2589; + airplane2587; +} +mist2614 = hope1839.mist1584; +potato2613 = mist2614; +function ship2606() -> Cats720 Cats720 { + cannon724: 18446744072828497516i32; + street721: (12270487583713200103i64 - increase1607); + fold725: cherries2612 = prison882; + chance722: potato2613.fiction1686; + cave723: 2678878357u32; +}; +function sister2608(able2609: u64,bell2610: u32) -> Father456 Father456 { + whip458: goldfish2611 = Giraffe251 { + cart255: experience1094; + coach253: grade1640; + company252: tiger2080; + pigs256: 16376518433542522042i64; + meeting254: 1741585270i32; + thread257: wire2308; + boys258: Ground237 { + baby241: secretary1857; + fly247: condition1744; + pin244: 10576677775390112997u64; + join239: bell2610; + joke243: 6101068055068504677u64; + lace240: 104i8; + art245: mailbox2309; + quartz238: grade1640; + banana242: 1780645335649918093i64; + achieve246: 3436426759967322994i64; + hands248: 3043139931957064955u64; + quilt249: 14779310147462656281i64; + transport250: Uncle122 { + turn126: 1453790254i32; + distribution123: brother834; + station124: leg1497; + fan129: brass2040; + snow125: request2533; + orange127: toad917; + run128: 18446744073709520407i16; + coal130: void(); + }; + }; + }; + fifth457: 14336532136702788747u64; +}; +yak2598 = { + oatmeal2607 = sister2608({ + committee1698; + receipt792; + 2374384356u32; + 1139514593u32 + },2186151177u32); + ship2606().fold725.stage350; + void(); + Fowl461 { + curve462: weather1645; + existence463: maid1746; + feet464: 18446744073709551526i8; + leg466: woman1614; + clocks467: oatmeal2607; + net465: 9754u16; + }.leg466 +}; +function distribution2582(lunch2600: u64,goose2601: i64,start2602: i8,wire2603: i16,education2604: u32) -> Sand141 smell2605 = prison882.robin353; +airplane2501 = distribution2582({ + rabbits2596 = houses2595; + crow2594 = houses2595 = rabbits2596; + relation2593 = crow2594.print2381; + bite2591 = care2592 = relation2593; + eye2585 = Eye2586 { + pin2588: 1837u16; + circle2590: Servant2377 { + work2378: meal2291; + print2381: bite2591; + paper2380: 24313i16; + grain2379: arch2416; + }; + stage2589: loss1695; + airplane2587: 3249320024331491414u64; + }.circle2590; + arithmetic2584 = eye2585; + prose2583 = arithmetic2584; + void(); + void(); + prose2583.work2378 +},example2597 = Ground237 { + baby241: ground2502; + fly247: rock1004; + pin244: 13791242726275046264u64; + join239: 3426808103u32; + joke243: 4786376067744300015u64; + lace240: 70i8; + art245: wire2308; + quartz238: 18446744073433056532i32; + banana242: clouds1420; + achieve246: 448977723688481645i64; + hands248: plane2039; + quilt249: increase1607; + transport250: yak2598; +}.achieve246,{ + (4571091309075270164u64 * 14983582267921375585u64); + 3594i16; + 5877929905037004183i64 +},effect2599 = farmer1397.brake86,Mint1788 { + holiday1793: pet1316; + driving1794: Detail1771 { + alley1783: Bag1767 { + death1769: finger1315; + quicksand1770: team1741; + death1768: wall2007; + }; + building1776: leg1497; + space1775: cause2079; + deer1777: 5718904586934790426i64; + ants1780: 25270i16; + cattle1781: 3667155801963053353i64; + trucks1772: pets890; + government1779: finger1315; + acoustics1782: brother834; + egg1773: amusement887; + growth1778: 8463656336130004342u64; + use1774: wrist790; + }; + animal1790: loss1695; + doll1791: 60332u16; + memory1789: slave2214; + quartz1792: 3186574290u32; +}.quartz1792).sister54; +function squirrel2575(spy2577: i8,ladybug2578: u8,fireman2579: void,desire2580: u64,light2581: i16) -> i32 18446744071838568261i32; +function eye2569(button2571: i16,alley2572: i64,position2573: u16) -> Father456 { + ((sail1994 * 875308280i32) / hammer1349); + bubble2574 = Answer1090 { + sponge64: name2287; + song57: price2316; + breath56: 18446744073709551555i8; + stamp58: anger596; + scarf59: account1174; + trade63: business2306; + downtown65: 4979u16; + change60: 18446744073709551583i8; + grip61: 18446744073709529544i16; + lamp62: void(); + nerve55: hope1122; + }.lamp62; + squirrel2575(stew2232,sticks2089,Fork1583 { + mist1584: void(); + trees1585: swing2292; + servant1586: Driving692 { + boot697: 12363822892979910678i64; + cub695: wrist790; + stop694: 8698376781617613596u64; + geese693: 46876u16; + pie698: 10171038180244013858i64; + action699: 1396046669i32; + box696: 14844u16; + plants700: fear1395; + }; + }.mist1584,{ + lock1173; + 28610i16; + 311186179i32; + things1644 + },twist2576 = 31827i16) +}; +function cows2503(eggs2565: u32,lunch2566: u16,friction2567: i64,bee2568: i64) -> Father456 eye2569((vegetable2031.weather1908 - airport2570 = 16050i16),copy1689,{ + business2306; + receipt792 +}.art245); +struct Hospital2516 { + fire2518: u16; + house2519; + turn2517; + name2520; +} +struct Brush2521 { + smoke2522: u64; + calendar2523: u16; + silk2524: Hospital2516; +} +struct owl2552 { + bedroom2550; + cherry2547: u64; + stem2548: u16; + value2549; +} +function pig2558(idea2561: u8,window2562: i32,money2563: u16,zinc2564: u64) -> i64 vegetable2031.milk1904; +function stew2557(crayon2559: i64) -> u16 yam2560 = 18317409171023218698u64; +function plantation2554(alarm2556: u64) -> u16 stew2557(pig2558(20u8,edge521.coach253,35308u16,17102628960045835472u64)); +teeth2545 = { + { + Answer1090 { + sponge64: 17692048861731679220i64; + song57: brass2040; + breath56: wrist790; + stamp58: 6504140120297515279u64; + scarf59: 5614i16; + trade63: business2306; + downtown65: 23288u16; + change60: stew2232; + grip61: uncle2009; + lamp62: void(); + nerve55: hope1122; + }.lamp62; + { + nail2546 = { + lock1173; + help793; + void() + }; + { + void(); + 242u8; + void(); + void(); + void() + }; + { + void(); + receipt792; + 395841438818172636i64; + receipt792; + 3865848988815116947u64 + }; + 18446744073709522368i16; + nail2546.dust50 + } + }; + 80i8; + 12591941147923959543u64; + (({ + metal2551 = owl2552 { + bedroom2550: 1852771868533756210i64; + cherry2547: 433555137756603796u64; + stem2548: 21402u16; + value2549: 28922u16; + }; + void(); + metal2551; + receipt792; + hope1122 + } / wire2308) / square2553 = plantation2554(7635454741105368521u64)); + feet2555 = void() +}; +function governor2542(verse2543: u32) -> Control1590 Control1590 { + songs1591: void(); + development1593: { + committee1698; + void() + }.lamp62; + winter1594: treatment2284; + taste1595: Fork1583 { + mist1584: void(); + trees1585: 152u8; + servant1586: tooth2544 = Driving692 { + boot697: chairs1873; + cub695: yoke1022; + stop694: 8197304710021896885u64; + geese693: 44281u16; + pie698: surprise2041; + action699: 18446744072362907183i32; + box696: art1743; + plants700: Cable92 { + patch97: cub1979; + lock98: coal1020; + popcorn93: condition2240; + hydrant95: nerve1108; + summer94: competition1083; + coast99: Judge45 { + dust50: 18446744073709525676i16; + pleasure69: goldfish856; + babies48: treatment2284; + icicle46: straw1740; + tray49: 114u8; + flavor70: 18446744073709551527i8; + stew71: 673782259i32; + order47: 72i8; + flame51: 28071i16; + adjustment52: Sand141 { + sister54: teeth2545; + cart68: measure1991; + van53: pigs1701; + guitar66: goldfish856; + ghost67: month2289; + }; + }; + cord96: measure1991; + }; + }; + }; + grain1592: 67i8; +}; +prose2530 = { + range2541 = Cub1830 { + plot1831: 18446744073546795529i32; + birth1832: governor2542(balls2226); + }; + 31899u16; + line2540 = -toothpaste2211; + range2541.birth1832.songs1591 +}; +function act2532(war2536: i8,ship2537: u16,sail2538: i32,hot2539: i16) -> i64 7952271270144015735i64; +servant2528 = silk2529 = Peace2389 { + frogs2397: 10803864468654234284u64; + curve2390: name2287; + rice2393: brass1421; + start2398: Peace2389 { + frogs2397: 5584314903368351772u64; + curve2390: 13818358446870706114i64; + rice2393: plants2422; + start2398: prose2530; + quicksand2394: wire2308; + rain2391: 5247685747267087803i64; + cactus2395: writer1399; + button2392: measure1991; + brass2396: trade2008; + }.start2398; + quicksand2394: { + 33797u16; + (tendency908 / smell1860); + beggar2531 = hope1122; + receipt792 + }; + rain2391: surprise2041; + cactus2395: 18446744073709548725i16; + button2392: slave2299; + brass2396: act2532(request2533 = comparison1889,spring2534 = 6441u16,18446744073384399276i32,birthday2535 = uncle2009); +}; +snails2525 = Hospital2516 { + fire2518: (61034u16 + { + print fact1172; + 22786i16 + }); + house2519: (Banana1053 { + story1063: Note1190 { + unit1067: brother834; + bike1066: smell1860; + food1068: cause2079; + burst1065: 225u8; + coil1064: 9094997403298350617i64; + }; + amusement1055: art1743; + wrench1062: wren2497; + boats1071: hot868; + action1072: 18446744073709540830i16; + reason1073: North85 { + day87: 1305591288i32; + brake86: 4860i16; + apple88: art1743; + }; + vessel1069: 18057497959713673043u64; + driving1060: 139u8; + leather1061: 4162i16; + waves1070: brother834; + visitor1056: 18446744071847972571i32; + committee1054: quiver1858; + locket1059: 16532442400503409299u64; + cast1058: slave2214; + tank1057: toothpaste2211; + }.boats1071 - hydrant2526 = plane2039); + turn2517: business2527 = price2316; + name2520: servant2528; +}; +function addition2510(nest2513: i64,flower2514: u64) -> Peace2389 watch2515 = Brush2521 { + smoke2522: brass2040; + calendar2523: pet1316; + silk2524: snails2525; +}.silk2524.name2520; +function pump2507(treatment2508: i16) -> Connection750 Connection750 { + surprise758: 6492468466925829576i64; + price756: { + lock1173; + void(); + void(); + 13908i16 + }.fan129; + skirt759: condition2240; + giraffe763: { + void(); + pets890 + }; + punishment765: 27819u16; + leg757: { + truck2509 = 136u8; + void() + }; + play762: addition2510(increase1607,4568472891798552374u64).frogs2397; + son766: { + burn1828.development1593; + distribution1709.stamp58 + }; + baby761: 215u8; + berry751: trip2511 = porter1927; + vegetable760: 164u8; + vase764: cats2512 = measure1804.spy1797; +}; +class580 = Fowl461 { + curve462: (calendar522.boys258.baby241 * Tree348 { + vessel349: 31907u16; + stage350: 3009401960243415486u64; + worm351: 3696u16; + chalk352: sail1994; + robin353: Sand141 { + sister54: airplane2501; + cart68: tiger2080; + van53: 22087u16; + guitar66: rock1004; + ghost67: 9599152318927795877i64; + }; + }.worm351); + existence463: lock1173.weather1908; + feet464: camp2212; + leg466: ground2502 = { + 223u8; + receipt792; + 134u8; + help793; + wrist790 + }; + clocks467: cows2503(finger2504 = { + smell1860; + void(); + void(); + void(); + void() + },{ + ocean2505 = experience1094; + burn1828.songs1591; + receipt792; + void(); + hope1839.mist1584 + },woman2506 = 2343082411222473594i64,2388274116627208270i64); + net465: pump2507(measure1991).punishment765; +}; +sheet581 = 18446744073360961012i32; +jeans582 = 1176784054i32; +parent583 = 1474147503u32; +page584 = (3888818982u32 + 58u8); +string585 = 7715000977821085937u64; +profit586 = 74i8; +rat587 = relation2225; +needle588 = soup2500 = condition2240; +list589 = 2623910800901527295i64; +pickle590 = 12948635491740615982u64; +power593 = quiver1175; +loaf594 = 8192669843977119819u64; +function force595(ant2499: i16) -> u32 leg1497; +yoke598 = 18446744073709551540i8; +hobbies599 = wren2497 = Uncle122 { + turn126: side2498 = 10337102i32; + distribution123: 18446744073709551503i8; + station124: goldfish856; + fan129: 2687241230u32; + snow125: 79i8; + orange127: { + void(); + void(); + receipt792; + 4269105727u32 + }; + run128: 20414i16; + coal130: print tiger2080; +}.turn126; +quilt600 = camp2212; +square601 = reading2300.fly247; +page602 = pin2496 = { + print 15206554873651105821u64; + jam2111.rainstorm1474; + art1743 +}; +wing603 = { + { + print trade2008; + { + 1894922191u32; + void(); + bath2050; + void() + }; + { + void(); + comparison1889; + rabbits2045 + }; + lizards2495 = 6794936223620808344i64 + }; + print bomb1097; + void() +}.bike2013; +river604 = wall2007; +rabbits606 = sticks2089; +sponge2491 = prison2492 = { + void(); + sheet2493 = pigs1701; + print swing2292; + song2494 = 209u8; + cause2079 +}; +function driving605(trees2487: i16,oatmeal2488: i8) -> Picture29 ray2489 = winter2490 = sponge2491; +record607 = arch2416; +function step651(honey2478: u64,alley2479: u16,jellyfish2480: i64,kittens2481: u64,treatment2482: u64,scarecrow2483: u32,board2484: u32,women2485: i8) -> u8 balance2486 = committee1698; +function reason663(page2476: i64,dogs2477: u64) -> i64 15918693683909381969i64; +struct Ground732 { + jeans703; + wing705: i8; + plane707: i16; + books706: u16; + meat704: u32; +} +function shelf702(legs2474: struct {meat704: u32; +jeans703: _; +wing705: i8; +plane707: i16; +books706: u16;}) -> i8 reading2475 = { + Judge45 { + dust50: 18446744073709524287i16; + pleasure69: 1823878476u32; + babies48: eyes1242; + icicle46: 18446744073709542084i16; + tray49: 76u8; + flavor70: stew2232; + stew71: 786178500i32; + order47: brother834; + flame51: 8334i16; + adjustment52: cats1547; + }.dust50; + Control1590 { + songs1591: receipt792; + development1593: help793; + winter1594: dress1612; + taste1595: hope1839; + grain1592: plants2422; + }.development1593; + void(); + 12037257016331615069u64; + (step2305 - bells1703); + pigs1422 +}; +struct thing711 { + change60: i16; + grip61: i16; + nerve55: i16; + sponge64; + downtown65: u16; + song57: u64; + breath56: Judge45; + trade63: void; + stamp58: u64; + lamp62: i64; + scarf59; +} +struct Cats720 { + cannon724: i32; + street721: i64; + fold725: Tree348; + chance722: u32; + cave723; +} +function death2430(yoke2464: struct {joke2440: i8; +tin2445: i64; +crown2441: _; +gate2439: i64; +quartz2446: u64; +fire2432: struct {boot2438: i64; +page2435: _; +unit2434: i16; +rabbits2433: i16; +tooth2436: u64; +request2437: _;}; +reaction2442: u32; +giraffe2443: i16; +things2444: i16; +beginner2431: i64;},furniture2465: i64,bears2466: u16,uncle2467: i64,reaction2468: Animal1913,birth2469: i16,railway2470: u64,food2471: i64) -> Uncle122 Uncle122 { + turn126: door2472 = 1512691531i32; + distribution123: work1306.feet464; + station124: flavor2473 = { + void(); + babies2143; + canvas433; + 17731i16; + 14788u16 + }; + fan129: 3716916924173035881u64; + snow125: 99i8; + orange127: 18446744073709539654i16; + run128: 18446744073709522412i16; + coal130: void(); +}; +struct tree2463 { + unit2434: i16; + page2435: u16; + tooth2436: u64; + boot2438: i64; + rabbits2433: i16; + request2437; +} +struct pump2461 { + fifth2459: u64; + attention2453: i16; + suggestion2454; + magic2450: u16; + town2452; + pleasure2460; + light2457: i16; + glove2458: i64; + stream2449: u16; + society2456: i16; + weight2451; + girls2455: i64; +} +struct dog2448 { + tin2445: i64; + giraffe2443: i16; + joke2440: i8; + things2444: i16; + quartz2446: u64; + crown2441: struct {stream2449: _; + magic2450: u16; + light2457: i16; + glove2458: _; + fifth2459: u64; + pleasure2460: _; + society2456: i16; + weight2451: _; + girls2455: i64; + attention2453: i16; + suggestion2454: _; + town2452: u16;}; + fire2432: struct {boot2438: i64; + page2435: _; + unit2434: i16; + rabbits2433: i16; + tooth2436: u64; + request2437: _;}; + reaction2442: u32; + beginner2431; + gate2439: i64; +} +function lip2336(turn2423: u32,jellyfish2424: u64,question2425: i32,gold2426: u32) -> Answer1090 Answer1090 { + sponge64: { + receipt792; + void() + }; + song57: (balls2226 * plane2039); + breath56: (); + stamp58: help793.stamp58; + scarf59: { + 18446744073709519546i16; + void(); + receipt792 + }; + trade63: help793; + downtown65: apparatus2427 = (21980u16 - woman1614); + change60: pain2428 = 54i8; + grip61: { + hope2462 = tree2463 { + unit2434: writer1399; + page2435: 53940u16; + tooth2436: 4418668011907157898u64; + boot2438: 5517267193493554586i64; + rabbits2433: 12571i16; + request2437: smell1860; + }; + honey2447 = dog2448 { + tin2445: surprise2041; + giraffe2443: 7278i16; + joke2440: fact1172; + things2444: 23317i16; + quartz2446: tendency908; + crown2441: pump2461 { + fifth2459: banana2307; + attention2453: 6083i16; + suggestion2454: 28442i16; + magic2450: 53807u16; + town2452: 49087u16; + pleasure2460: mailbox2309; + light2457: straw1740; + glove2458: trade2008; + stream2449: 41820u16; + society2456: pigs1422; + weight2451: 65361u16; + girls2455: 9601089210821686781i64; + }; + fire2432: hope2462; + reaction2442: rock1004; + beginner2431: thumb888; + gate2439: amusement887; + }; + apple2429 = death2430(honey2447,2522255615393759903i64,canvas433,10172012835422718466i64,Animal1913 { + toys1903: plane2039; + boats1902: 15030705586421065894u64; + number1905: 18446744073709519007i16; + example1900: 31246u16; + dime1909: 15037685154851581786i64; + playground1907: jellyfish2424; + milk1904: babies2143; + weather1908: 31837i16; + monkey1906: slave2299; + horse1910: toothpaste2211; + bread1901: price2316; + },18446744073709520824i16,banana2307,2194792835490812616i64); + apple2429.run128 + }; + lamp62: void(); + nerve55: Animal1913 { + toys1903: 5642458537244933711u64; + boats1902: rabbits2045; + number1905: 18446744073709531797i16; + example1900: secretary1857; + dime1909: 13755747483568304997i64; + playground1907: 10301385420356361061u64; + milk1904: 13420588871870668005i64; + weather1908: sneeze830; + monkey1906: 177u8; + horse1910: 7526i16; + bread1901: 638582725963381164u64; + }.horse1910; +}; +heat2344 = Detail1771 { + alley1783: { + brain2414 = { + uncle2009; + void(); + void(); + business2306 + }; + force2413 = void(); + brain2414.coal130; + salt1925.holiday1793; + error2415 = slave2214 + }; + building1776: ({ + void(); + business2306; + thumb888; + fact1172 + } * stick1983.giraffe763); + space1775: (coal1020 - 18446744073709538320i16); + deer1777: Animal1913 { + toys1903: 434139702986543431u64; + boats1902: rabbits2045; + number1905: 18446744073709546805i16; + example1900: pet1316; + dime1909: amusement887; + playground1907: button2241; + milk1904: 9392058134173884493i64; + weather1908: cause2079; + monkey1906: bomb1097; + horse1910: writer1399; + bread1901: rabbits2045; + }.dime1909; + ants1780: arch2416 = 18446744073709551598i8; + cattle1781: (2428i16 / clock2303); + trucks1772: { + void(); + Answer1090 { + sponge64: 5485033577464000081i64; + song57: 15261344702767320915u64; + breath56: yoke1022; + stamp58: slave2214; + scarf59: 18446744073709541540i16; + trade63: void(); + downtown65: art1743; + change60: 2i8; + grip61: board2043; + lamp62: void(); + nerve55: maid1746; + }.lamp62; + alley2417 = 5901605143043455751u64; + { + increase1607; + 2112164044u32; + receipt792; + competition1083 + } + }; + government1779: { + operation2418 = rabbits2045; + receipt792; + () + }; + acoustics1782: wrist790; + egg1773: { + rock1004; + void(); + distribution1709.trade63; + growth2419 = void(); + Fork1583 { + mist1584: business2306; + trees1585: 9141998746871203299i64; + servant1586: pencil2144; + }.mist1584 + }; + growth1778: { + string2420 = mask2124; + void(); + step2305 + }; + use1774: number2421 = plants2422 = 19i8; +}.alley1783; +struct Peace2389 { + frogs2397: u64; + curve2390: i64; + rice2393; + start2398: Expert1681; + quicksand2394: u16; + rain2391; + cactus2395: i16; + button2392: i16; + brass2396: i64; +} +struct visitor2402 { + crime314: i16; + development310; + observation311: u8; + scissors313: i64; + calculator312; +} +struct boats2406 { + wire2403: i32; + fly2404: i64; +} +function growth2408(relation2409: void,behavior2410: u8,orange2411: i64,engine2412: i64) -> i16 25166i16; +function space2400(kittens2401: i8) -> Band315 Band315 { + answer322: visitor2402 { + crime314: mailbox1664.leather1061; + development310: (18446744073709551607i8 - 18446744073709551494i8); + observation311: 86u8; + scissors313: smash1096; + calculator312: { + help793; + void() + }; + }; + crow316: 18870u16; + umbrella317: 10230965763247850566u64; + summer321: { + feast2405 = boats2406 { + wire2403: grade1640; + fly2404: 1332890801665354132i64; + }; + receipt792; + { + finger1315; + 33798u16 + } + }; + substance319: goldfish856; + earthquake320: heat2407 = growth2408(business2306,104u8,wall2007,7111239015297311060i64); + exchange318: clock2303; +}; +join2374 = { + { + void(); + tomatoes2399 = 18393515420862305888i64; + lock1173; + business2306; + 9300256928945031444u64 + }; + space2400(hammer1349).summer321; + void() +}.start2398; +function wine2386(fact2387: u16) -> struct {scarf59: i16; +grip61: i16; +change60: _; +song57: u64; +stamp58: _; +nerve55: i16; +lamp62: _; +trade63: void; +sponge64: _; +downtown65: u16; +breath56: _;} growth2388 = basket914.sister54; +roll2385 = wine2386(31489u16); +function quince2373(shock2384: u64) -> i8 Ground237 { + baby241: dress1612; + fly247: 1916956263u32; + pin244: { + void(); + glue2229; + 18446744073709525935i16; + 1605621809i32 + }; + join239: 750711298u32; + joke243: hot868; + lace240: Judge45 { + dust50: twig1739; + pleasure69: leg1497; + babies48: 26u8; + icicle46: twig1739; + tray49: 236u8; + flavor70: 99i8; + stew71: yoke1280; + order47: 18446744073709551529i8; + flame51: board2043; + adjustment52: Sand141 { + sister54: roll2385; + cart68: 18446744073709527151i16; + van53: 54696u16; + guitar66: condition1744; + ghost67: 8513677685743384716i64; + }; + }.order47; + art245: 16543u16; + quartz238: grade1640; + banana242: 8302120031584288633i64; + achieve246: (month2289 + babies2143); + hands248: brass2040; + quilt249: 60821u16; + transport250: Uncle122 { + turn126: 1377682356i32; + distribution123: 18446744073709551550i8; + station124: rock1004; + fan129: price2316; + snow125: 53i8; + orange127: twig1739; + run128: 18446744073709544650i16; + coal130: void(); + }; +}.lace240; +struct Servant2377 { + work2378: i64; + print2381: Bells2362; + paper2380: i16; + grain2379: i8; +} +struct neck2382 { + crown13: i16; + pickle12: i16; + pizzas6: u32; + trip3: u64; + disease4: i8; + temper28; + direction11; + size10; + twist5: u64; + earthquake8; + father9: u64; + drop14: u8; + poison2: u64; + watch7: i32; + fight15: struct {territory16: _; + camp18: i16; + action22: u16; + grade27: u8; + letter26: u32; + surprise17: _; + fold24: i64; + mice23: _; + stranger25: i64; + pickle20: u64; + channel21: _; + love19: _;}; +} +struct development2383 { + camp18: i16; + grade27: u8; + surprise17: i8; + fold24: i64; + territory16: u16; + letter26: u32; + action22: u16; + mice23: i32; + channel21: i16; + pickle20: u64; + stranger25: i64; + love19; +} +function snow2372(bucket2375: i8,sail2376: u32) -> Bells2362 Servant2377 { + work2378: clouds1420; + print2381: Bells2362 { + pan2363: payment2037; + knee2364: Picture29 { + start30: 1905577564745859849u64; + girls31: neck2382 { + crown13: 8831i16; + pickle12: 18446744073709533679i16; + pizzas6: rock1004; + trip3: 7176483689319259868u64; + disease4: 18446744073709551510i8; + temper28: 18446744073406391112i32; + direction11: 8766259145981036283i64; + size10: 28657i16; + twist5: rabbits2045; + earthquake8: bath2050; + father9: 12987387980284590300u64; + drop14: 16u8; + poison2: price2316; + watch7: 18446744072224864561i32; + fight15: development2383 { + camp18: 10602i16; + grade27: 54u8; + surprise17: 86i8; + fold24: increase1607; + territory16: dress1612; + letter26: 1756235597u32; + action22: 10984u16; + mice23: lift2242; + channel21: straw1740; + pickle20: hot868; + stranger25: wall2007; + love19: 15296602543502690637u64; + }; + }; + }; + }; + paper2380: North85 { + day87: 834287196i32; + brake86: cause2079; + apple88: 26905u16; + }.brake86; + grain2379: 18446744073709551566i8; +}.print2381; +apple2371 = snow2372(quince2373(join2374.payment1682),goldfish856); +function ear2367(game2369: i32) -> Bells2362 Bells2362 { + pan2363: mouth2370 = bean1697; + knee2364: apple2371.knee2364; +}; +struct Bells2362 { + pan2363; + knee2364: Picture29; +} +whistle2360 = { + mask2366 = ear2367(cloth2368 = { + clock2303; + receipt792; + smash1096; + brass2040; + 1000802014021021642u64 + }); + stem2365 = mask2366; + beam2361 = stem2365.knee2364; + beam2361.start30 +}; +function stick2347(bean2355: u16,men2356: u64,form2357: i64) -> u64 (vase2358 = smell1860 - rule2359 = whistle2360.payment1682); +function mask2351(memory2352: i16,jewel2353: i8,steam2354: i8) -> void { + void(); + weather1645 +}.development1593; +function lettuce2345(hand2348: u16,plants2349: i64,spark2350: u64) -> void mask2351(brass1421,{ + business2306; + rock1004 +},Uncle122 { + turn126: relation2225; + distribution123: camp2212; + station124: rock1004; + fan129: 9555359205311493609u64; + snow125: brass1421; + orange127: clock2303; + run128: 18446744073709547261i16; + coal130: void(); +}.distribution123); +function lumber2339(boys2341: u64,tiger2342: i32) -> Uncle122 Uncle122 { + turn126: lift2242; + distribution123: 48i8; + station124: idea2343 = Doll1647 { + truck753: boys2341; + furniture754: 3041364004u32; + watch752: 18446744073586478732i32; + walk755: name2287; + }.furniture754; + fan129: 11721274741549484954u64; + snow125: (); + orange127: Hot2275 { + chalk2278: 2759512166089357990u64; + paint2276: wrist790; + wound2279: Bath2102 { + measure2103: 42798u16; + rake2104: sneeze830; + lake2105: Screw1951 { + spade1956: 11675613413317806219u64; + basin1952: Song1795 { + popcorn1796: prison882; + news1800: Mint1788 { + holiday1793: pigs1701; + driving1794: Detail1771 { + alley1783: heat2344; + building1776: 182781940u32; + space1775: 27619i16; + deer1777: 775351537536507487i64; + ants1780: toothpaste2211; + cattle1781: 5346987503432858377i64; + trucks1772: measure1991; + government1779: 35103u16; + acoustics1782: 99i8; + egg1773: 13268831514745221727i64; + growth1778: 10518793028779494757u64; + use1774: 95i8; + }; + animal1790: 19u8; + doll1791: 44078u16; + memory1789: 15745195705567899581u64; + quartz1792: 412903150u32; + }; + spy1797: 49521u16; + joke1799: smell1860; + owl1798: smash1096; + }; + cork1959: Profit1891 { + governor1894: 10951225775912414381i64; + insurance1892: 10793824034891461194i64; + surprise1893: dress1612; + glass1895: 1467288094i32; + sign1896: mailbox1664; + }; + sun1954: 83u8; + reading1955: amusement887; + card1953: 1234936718i32; + wood1957: copy1689; + page1958: 10893025634517481549i64; + }; + }; + experience2277: 18446744072932207068i32; + }.wound2279.rake2104; + run128: 18446744073709520951i16; + coal130: lettuce2345(trick2346 = 24901u16,(copy1689 + 292132552825055617i64),stick2347(finger1315,plane2039,nerve1108)); +}; +function rub2333(wall2337: i64) -> Uncle122 government2338 = lumber2339(bushes2340 = anger596,1110503853i32); +can2332 = rub2333({ + (hot2334 = pet1316 * 31402u16); + fruit2335 = 18i8; + slave2214; + void(); + lip2336(committee2302,487679392380684535u64,18446744072984743202i32,goldfish856).trade63 +}); +sound2328 = Deer969 { + calendar966: can2332.distribution123; + birthday964: 3227u16; + pancake968: 12597972i32; + wind967: 18446744072551206526i32; + direction965: Cub1830 { + plot1831: lift2242; + birth1832: burn1828; + }.birth1832.grain1592; +}; +function observation2329(good-bye2330: i8) -> u32 carriage2331 = 1480769202u32; +function net2324(jail2325: i16,ant2326: u8,grape2327: i16) -> Uncle122 Uncle122 { + turn126: { + void(); + void(); + print smash1096 + }; + distribution123: sound2328.calendar966; + station124: observation2329(comparison1889); + fan129: (); + snow125: Friction1287 { + page1289: 40i8; + fight1294: 1551314236890163395i64; + crayon1291: 18446744072082417514i32; + color1288: 7233534447966235627i64; + hate1293: 63013u16; + observation1290: 234u8; + flight1295: Answer1090 { + sponge64: chairs1873; + song57: 10716121226808786897u64; + breath56: 18446744073709551518i8; + stamp58: 9427193673189159553u64; + scarf59: 18446744073709538793i16; + trade63: receipt792; + downtown65: 12386u16; + change60: 18446744073709551571i8; + grip61: maid1746; + lamp62: void(); + nerve55: maid1746; + }; + rabbit1292: step2305; + }.page1289; + orange127: 19235i16; + run128: uncle2009; + coal130: void(); +}; +function activity2314(hair2321: i32,arithmetic2322: u8,quince2323: i16) -> Uncle122 net2324(eggs2035,{ + { + eyes1242; + void(); + void(); + receipt792 + }; + void(); + void(); + { + help793; + woman1614 + }; + (39i8 + stew2232) +},print condition1744); +function coat2315(unit2318: u64,wilderness2319: u64) -> u64 step2320 = unit2318; +example2273 = Answer1090 { + sponge64: Ground237 { + baby241: mailbox2309; + fly247: bells1703; + pin244: 16114354627712901496u64; + join239: { + void(); + void(); + help793; + 14973550575014149975i64; + lock1173 + }; + joke243: 1367461121425181543u64; + lace240: hammer1349; + art245: { + help793; + 23i8; + 18446744073010747469i32; + receipt792; + lock1173; + slave2214; + void() + }; + quartz238: { + 33909u16; + help793 + }; + banana242: 9302287282174772882i64; + achieve246: 15062855272144462396i64; + hands248: smell1860; + quilt249: { + wall2007; + 692711300u32; + help793; + lock1173 + }; + transport250: activity2314(process1265,slave2299,13557i16); + }.achieve246; + song57: coat2315(brass2040,5170185769046216167u64); + breath56: brass1421; + stamp58: price2316 = 15971743186819062251u64; + scarf59: Judge45 { + dust50: pets890; + pleasure69: 3372088824u32; + babies48: 7u8; + icicle46: hope1122; + tray49: 230u8; + flavor70: comparison1889; + stew71: 2043092680i32; + order47: 22i8; + flame51: 6622i16; + adjustment52: Sand141 { + sister54: Answer1090 { + sponge64: 4646019502533631798i64; + song57: anger596; + breath56: 18446744073709551523i8; + stamp58: slave2214; + scarf59: 26466i16; + trade63: void(); + downtown65: 17263u16; + change60: comparison1889; + grip61: maid1746; + lamp62: void(); + nerve55: 30990i16; + }; + cart68: 18446744073709530950i16; + van53: finger1315; + guitar66: 1693623681u32; + ghost67: 18131241853856616912i64; + }; + }.icicle46; + trade63: void(); + downtown65: art1743; + change60: 52i8; + grip61: { + 9998469431477596370i64; + print (anger596 * banana2307) + }; + lamp62: business2306; + nerve55: society2317 = { + void(); + smile1691; + lock1173 + }.action1072; +}; +struct Hot2275 { + chalk2278; + paint2276: i8; + wound2279: Bath2102; + experience2277: i32; +} +function smoke2282(ship2312: void) -> i16 test2313 = brass1421; +field2285 = { + fear1395.summer94; + point2310 = parcel2311 = void(); + 27552i16 +}; +rain2301 = Uncle122 { + turn126: 18446744073663540217i32; + distribution123: (stew2232 + 46i8); + station124: nail832; + fan129: 7444832907692772891u64; + snow125: brother834; + orange127: 18446744073709547064i16; + run128: { + { + quiver1175; + 33661u16; + help793 + }; + { + sneeze830; + cub1979; + 238u8; + stew2232; + 21362u16; + void(); + void(); + 18446744073709544740i16 + } + }; + coal130: burn1828.songs1591; +}; +girls2304 = Doll1647 { + truck753: banana2307 = 3578256012747930929u64; + furniture754: wire2308 = mailbox2309 = woman1614; + watch752: 979958681i32; + walk755: 14842889725791707863i64; +}; +detail2294 = Uncle122 { + turn126: -slave2299 = bomb1097; + distribution123: reading2300 = Ground237 { + baby241: 37591u16; + fly247: goldfish856; + pin244: slave2214; + join239: balls2226; + joke243: 6910678088043934216u64; + lace240: stew2232; + art245: secretary1857; + quartz238: lift2242; + banana242: clouds1420; + achieve246: 8329808021906037822i64; + hands248: rabbits2045; + quilt249: start1419; + transport250: rain2301; + }.lace240; + station124: committee2302 = condition1744; + fan129: grade1619; + snow125: -((18446744073709551599i8 * 10i8) * { + camp2212; + receipt792; + coal1020 + }); + orange127: toothpaste2211; + run128: Furniture1362 { + cent1369: clock2303 = measure1991; + arm1366: 48311u16; + spark1363: committee2302; + fiction1370: art1743; + sound1373: Furniture1362 { + cent1369: 10747i16; + arm1366: 36616u16; + spark1363: 1187795705u32; + fiction1370: pigs1701; + sound1373: Connection750 { + surprise758: 11867204837334246932i64; + price756: brass2040; + skirt759: copy1689; + giraffe763: 1267548708u32; + punishment765: pet1316; + leg757: sail1994; + play762: 9082489848556295223u64; + son766: jam982; + baby761: nerve1108; + berry751: girls2304; + vegetable760: loss1695; + vase764: weather1645; + }; + beam1371: 83u8; + army1365: toad917; + joke1372: 252u8; + carpenter1368: balls2226; + clam1367: account1174; + hall1364: 18446744072166456567i32; + }.sound1373; + beam1371: step2305 = bean1697; + army1365: toad917; + joke1372: 244u8; + carpenter1368: Ground237 { + baby241: 302u16; + fly247: 3109066325u32; + pin244: tendency908; + join239: 1292382812u32; + joke243: 16608060051786763388u64; + lace240: brass1421; + art245: 2544u16; + quartz238: 320781331i32; + banana242: mask2124; + achieve246: condition2240; + hands248: smell1860; + quilt249: 7335887859032765306i64; + transport250: Uncle122 { + turn126: yoke1280; + distribution123: 36i8; + station124: 2047415285u32; + fan129: plane2039; + snow125: brass1421; + orange127: pets890; + run128: 6139i16; + coal130: receipt792; + }; + }.join239; + clam1367: 30918i16; + hall1364: lift2242; + }.cent1369; + coal130: business2306 = { + void(); + (secretary1857 - pigs1701) + }; +}; +weather2286 = driving2293 = Profit1891 { + governor1894: Doll1647 { + truck753: 7929740260258476415u64; + furniture754: 3905040474u32; + watch752: glue2229; + walk755: surprise2041; + }.walk755; + insurance1892: meal2291; + surprise1893: 60847u16; + glass1895: detail2294.turn126; + sign1896: { + uncle2298 = Deer969 { + calendar966: 43i8; + birthday964: weather1645; + pancake968: lift2242; + wind967: sail1994; + direction965: hammer1349; + }; + comparison2296 = moon2297 = uncle2298; + observation2295 = Spark1147 { + fruit1153: 142u8; + muscle1149: 13635262147401331143u64; + church1151: 18446744072477265044i32; + hate1154: 2319162185u32; + apparatus1155: comparison2296; + sneeze1148: 427012036128572022i64; + feather1152: 18446744073709548117i16; + rake1150: 1u8; + }; + treatment2284; + coal1020; + 18446744073709531954i16; + receipt792; + 39i8; + (slave2214 - tendency908); + Holiday2012 { + cook2016: observation2295; + bike2013: 24399i16; + drum2014: smell1860; + feet2015: eggs2035; + }.bike2013 + }; +}; +police2283 = Screw1951 { + spade1956: (vegetable2031.example1900 / 14406u16); + basin1952: Screw1951 { + spade1956: 5097385950086313723u64; + basin1952: Song1795 { + popcorn1796: Tree348 { + vessel349: 60688u16; + stage350: anger596; + worm351: 11774u16; + chalk352: 774094139i32; + robin353: basket914; + }; + news1800: Mint1788 { + holiday1793: 7954u16; + driving1794: Detail1771 { + alley1783: Bag1767 { + death1769: 26912u16; + quicksand1770: team1741; + death1768: 11675344899757006494i64; + }; + building1776: rock1004; + space1775: 24145i16; + deer1777: 4637619575483239741i64; + ants1780: experience1094; + cattle1781: thumb888; + trucks1772: toad917; + government1779: 16443u16; + acoustics1782: brass1421; + egg1773: cellar919; + growth1778: slave2214; + use1774: 1i8; + }; + animal1790: things1644; + doll1791: weather1645; + memory1789: button2241; + quartz1792: leg1497; + }; + spy1797: 8483u16; + joke1799: anger596; + owl1798: 205u8; + }; + cork1959: Profit1891 { + governor1894: 3560496165591759391i64; + insurance1892: 1545377488601931166i64; + surprise1893: 19154u16; + glass1895: quiver1858; + sign1896: Banana1053 { + story1063: Note1190 { + unit1067: hammer1349; + bike1066: anger596; + food1068: sneeze830; + burst1065: 225u8; + coil1064: increase1607; + }; + amusement1055: woman1614; + wrench1062: 37659333i32; + boats1071: 5296939660678060885u64; + action1072: 31452i16; + reason1073: North85 { + day87: 1237357260i32; + brake86: uncle2009; + apple88: weather1645; + }; + vessel1069: brass2040; + driving1060: 177u8; + leather1061: quiver1175; + waves1070: 10i8; + visitor1056: 1460334811i32; + committee1054: process1265; + locket1059: 12558686733693775183u64; + cast1058: 14026901636464383965u64; + tank1057: board2043; + }; + }; + sun1954: treatment2284 = 32u8; + reading1955: yoke1280; + card1953: { + sneeze830; + void(); + help793 + }; + wood1957: quiver1175; + page1958: Detail1771 { + alley1783: Bag1767 { + death1769: 17691u16; + quicksand1770: Band315 { + answer322: field2285; + crow316: 53263u16; + umbrella317: mitten2046; + summer321: 6753334943013244234i64; + substance319: leg1497; + earthquake320: 18446744073709542605i16; + exchange318: 18446744073709519914i16; + }; + death1768: 9458064797835255151i64; + }; + building1776: leg1497; + space1775: 7895i16; + deer1777: 7139454561148443993i64; + ants1780: 32695i16; + cattle1781: surprise2041; + trucks1772: quiver1175; + government1779: dress1612; + acoustics1782: stew2232; + egg1773: 3153117036005141150i64; + growth1778: 462847741828156020u64; + use1774: 3i8; + }.deer1777; + }.basin1952; + cork1959: weather2286; + sun1954: 145u8; + reading1955: name2287 = Giraffe251 { + cart255: 9165i16; + coach253: sail1994; + company252: tiger2080; + pigs256: 7665473433947487724i64; + meeting254: relation2225; + thread257: pigs1701; + boys258: icicle831; + }.pigs256; + card1953: meal2288 = receipt792; + wood1957: month2289 = bulb2290 = meal2291 = 12402200309747610936i64; + page1958: swing2292 = 17496520378372777545i64; +}; +copy2280 = Bath2102 { + measure2103: { + kettle2281 = { + void(); + void(); + clouds1420; + receipt792; + void() + }; + lock1173; + distribution1709.lamp62; + burn1828.songs1591; + kettle2281.coal130 + }; + rake2104: smoke2282(()); + lake2105: police2283; +}; +cap2274 = Hot2275 { + chalk2278: 15284960858546110725u64; + paint2276: fact1172; + wound2279: copy2280; + experience2277: sail1994; +}.wound2279.lake2105; +function mitten716(ear2271: u32,silver2272: u8) -> Sand141 Sand141 { + sister54: Sand141 { + sister54: example2273; + cart68: 18446744073709524794i16; + van53: dress1612; + guitar66: 3870682197u32; + ghost67: 6164270426636511521i64; + }.sister54; + cart68: { + bomb1097; + void() + }; + van53: Bath2102 { + measure2103: 51704u16; + rake2104: pigs1422; + lake2105: cap2274; + }.measure2103; + guitar66: 930922246u32; + ghost67: 12390005574837442039i64; +}; +struct Driving692 { + boot697: i64; + cub695: i8; + stop694: u64; + geese693: u16; + pie698; + action699: i32; + box696; + plants700: Cable92; +} +function rod689(net2269: i64,mountain2270: i16) -> u8 bath2050; +function stomach2249(mailbox2266: u8,reward2267: u32,blow2268: u64) -> i32 -18446744073709551565i8; +struct thought2258 { + amount2252: u64; + feast2256: i16; + recess2254: i64; + plough2255: i16; + hospital2250: i16; + beggar2251; + laborer2253; +} +function root2247(flame2260: i32,stitch2261: struct {amount2252: _; +recess2254: i64; +plough2255: i16; +hospital2250: i16; +beggar2251: i64; +laborer2253: void; +feast2256: _;},move2262: i8) -> Tree348 Tree348 { + vessel349: polish2263 = 65169u16; + stage350: Song1795 { + popcorn1796: prison882; + news1800: salt1925; + spy1797: woman1614; + joke1799: rabbits2045; + owl1798: bean1697; + }.joke1799; + worm351: ({ + void(); + lock1173; + 18446744073709550125i16 + } + balls1437.leg466); + chalk352: sail1994; + robin353: { + print 27u8; + passenger2264 = button2241; + attention2265 = help793 + }; +}; +function alley736(hands2246: u64) -> Tree348 root2247(town2248 = stomach2249(220u8,condition1744,16293544369976351118u64),religion2257 = thought2258 { + amount2252: hot868; + feast2256: 18446744073709525120i16; + recess2254: chairs1873; + plough2255: straw1740; + hospital2250: 18446744073709536723i16; + beggar2251: trade2008; + laborer2253: void(); +},-temper2259 = 18446744073709551521i8); +struct flock656 { + stamp58: i8; + trade63: void; + sponge64: i64; + downtown65: u16; + scarf59; + song57: u64; + breath56; + change60: u64; + grip61: i16; + lamp62: i64; + nerve55: i16; +} +struct Yoke743 { + school746: Fowl461; + pail744; + show745; +} +struct Connection750 { + surprise758: i64; + price756: u64; + skirt759; + giraffe763: u32; + punishment765: u16; + leg757: i32; + play762: u64; + son766; + baby761: i64; + berry751: struct {watch752: i32; + truck753: u64; + walk755: i64; + furniture754: u32;}; + vegetable760: u8; + vase764: u16; +} +struct park770 { + truck753: u64; + furniture754: u32; + watch752: i32; + walk755: i64; +} +function sweater779(account2243: u8,brass2244: u16,thought2245: i16) -> Sand141 Sand141 { + sister54: Sand141 { + sister54: Answer1090 { + sponge64: 15482822818452277570i64; + song57: tendency908; + breath56: yoke1022; + stamp58: 797444083247162265u64; + scarf59: 4445i16; + trade63: void(); + downtown65: woman1614; + change60: 18446744073709551604i8; + grip61: pets890; + lamp62: void(); + nerve55: toad917; + }; + cart68: straw1740; + van53: dress1612; + guitar66: 2552417467u32; + ghost67: mask2124; + }.sister54; + cart68: smell1860; + van53: pet1316; + guitar66: nail832; + ghost67: (111u8 + 197u8); +}; +page821 = 3572687315439227260i64; +shame833 = 228u8; +plough835 = lift2242 = quiver1858; +berry836 = clouds1420; +sky837 = 3603774716443756499i64; +struct dad853 { + furniture754: u32; + walk755; + watch752: i32; + truck753; +} +function ant855() -> struct {scarf59: i16; +grip61: i16; +change60: _; +song57: u64; +stamp58: _; +nerve55: i16; +lamp62: _; +trade63: void; +sponge64: _; +downtown65: u16; +breath56: _;} { + relation2225; + void(); + bells1095 +}.robin353.sister54; +rat857 = toothpaste2211; +elbow858 = Ground237 { + baby241: 7690u16; + fly247: (goldfish856 - condition1744); + pin244: 8901965243988558066u64; + join239: 125u8; + joke243: smell1860; + lace240: { + receipt792; + void(); + 15032601378368801093i64 + }; + art245: weather1645; + quartz238: (cub1979 / sail1994); + banana242: { + help793; + 1080239470889425390i64 + }; + achieve246: condition2240 = 8453304011339937903i64; + hands248: button2241 = 14937461140339942016u64; + quilt249: 18446744073709540799i16; + transport250: Uncle122 { + turn126: 18446744073582479929i32; + distribution123: comparison1889; + station124: 1963385405u32; + fan129: 9070610823085133841u64; + snow125: hammer1349; + orange127: 26830i16; + run128: 5540i16; + coal130: void(); + }; +}.fly247; +function quicksand2231(fall2238: u64,use2239: u64) -> u32 { + pet1316; + 8317432600897244624i64; + void(); + Friction1287 { + page1289: brother834; + fight1294: trade2008; + crayon1291: 18446744071696430627i32; + color1288: nerve1108; + hate1293: 28404u16; + observation1290: attack2125; + flight1295: distribution1709; + rabbit1292: 196u8; + }.flight1295.lamp62 +}; +function cover2233(acoustics2235: i16,robin2236: i64,knife2237: i8) -> i16 18446744073709522177i16; +reaction859 = Uncle122 { + turn126: glue2229 = 951882277i32; + distribution123: 116i8; + station124: condition1744; + fan129: { + comparison2230 = Uncle122 { + turn126: (1511094938i32 * sail1994); + distribution123: -brass1421; + station124: quicksand2231(10112303001311618691u64,16128200245061372579u64); + fan129: 18138255621224413793u64; + snow125: 18446744073709551503i8; + orange127: 18446744073709528743i16; + run128: 18446744073709522913i16; + coal130: help793; + }; + (smile1691 / (107u8 / payment2037)); + { + help793; + void() + }; + (); + Ground237 { + baby241: pet1316; + fly247: leg1497; + pin244: 319691458231099458u64; + join239: goldfish856; + joke243: 8757378111027576915u64; + lace240: yoke1022; + art245: 29045u16; + quartz238: grade1640; + banana242: breakfast889; + achieve246: 4012670853291473449i64; + hands248: 2930735441448677646u64; + quilt249: breakfast889; + transport250: comparison2230; + }.art245 + }; + snow125: (brother834 / stew2232 = fact1172); + orange127: cover2233(26174i16,15647290620607917267i64,37i8); + run128: straw1740; + coal130: { + fact1172; + 55726u16; + 34322u16; + hearing2118.clam1367; + goose2234 = help793; + leg1497; + void(); + { + 6676i16; + receipt792; + 18446744073709547050i16; + 3579i16; + void(); + canvas433; + coal1020; + loss1695; + 37u8 + } + }; +}; +paint2228 = { + receipt792; + 3184426413322620508i64 +}; +rose2223 = paint2228; +motion2224 = Uncle122 { + turn126: (relation2225 = (18446744071906336750i32 * cub1979) / yoke1280); + distribution123: { + 1587850146u32; + 959069992i32 + }; + station124: ((pigs1701 - balls2226 = 2225888704u32) / 1651631376u32); + fan129: slave2214; + snow125: brother834; + orange127: { + void(); + print 18446744073709530212i16 + }; + run128: { + 5i8; + plantation2227 = Answer1090 { + sponge64: 15589398175106310540i64; + song57: 1955625171290203479u64; + breath56: 106i8; + stamp58: 11946786198204099067u64; + scarf59: account1174; + trade63: lock1173; + downtown65: 32466u16; + change60: 83i8; + grip61: 18446744073709549640i16; + lamp62: void(); + nerve55: 18446744073709548648i16; + }.trade63 + }; + coal130: Control1590 { + songs1591: receipt792; + development1593: void(); + winter1594: bean1697; + taste1595: burn1828.taste1595; + grain1592: 18446744073709551549i8; + }.songs1591; +}; +function plane2221(face2222: u8) -> Giraffe251 Giraffe251 { + cart255: 15588i16; + coach253: 18446744073465718035i32; + company252: 28926i16; + pigs256: 13756397357833837433i64; + meeting254: 18446744072294758148i32; + thread257: 38176u16; + boys258: Ground237 { + baby241: { + lock1173; + 18446744073709522486i16; + 4509u16; + 2993771900206028729u64 + }; + fly247: rose2223.fiction1686; + pin244: (); + join239: 3967008091u32; + joke243: void(); + lace240: Note1190 { + unit1067: camp2212; + bike1066: anger596; + food1068: 22955i16; + burst1065: 221u8; + coil1064: surprise2041; + }.unit1067; + art245: 26234u16; + quartz238: (process1265 - 34667014i32); + banana242: 34656u16; + achieve246: 16677661778677890446i64; + hands248: 10145138100987632904u64; + quilt249: 12413787633246752486i64; + transport250: motion2224; + }; +}; +function party2218(houses2219: i64,education2220: i32) -> Ground237 plane2221(37u8).boys258; +function power860(swing2217: i8) -> Ground237 party2218(11393778181567959734i64,1574716005i32); +can861 = process1265; +function amusement2213(transport2215: i32) -> i32 { + Uncle122 { + turn126: 18446744073092759061i32; + distribution123: brass1421; + station124: goldfish856; + fan129: 5797154229470559004u64; + snow125: 18446744073709551538i8; + orange127: 5915i16; + run128: 18446744073709532937i16; + coal130: void(); + }.station124; + sand2216 = Answer1090 { + sponge64: 15755095919734830902i64; + song57: 11849547698223774510u64; + breath56: 103i8; + stamp58: tendency908; + scarf59: 18446744073709545915i16; + trade63: void(); + downtown65: art1743; + change60: 18446744073709551592i8; + grip61: experience1094; + lamp62: help793; + nerve55: 18446744073709547052i16; + }.lamp62; + void() +}; +country891 = Banana1053 { + story1063: { + maid1746; + bean1697; + void(); + 51852u16 + }; + amusement1055: 936u16; + wrench1062: grade1640; + boats1071: 13619558465432584371u64; + action1072: 18446744073709536490i16; + reason1073: mailbox1664.reason1073; + vessel1069: 14651u16; + driving1060: { + void(); + 9667935163219653687u64; + help793; + 3054694533097516316u64 + }; + leather1061: toothpaste2211 = 9174i16; + waves1070: camp2212 = comparison1889; + visitor1056: 1114111190i32; + committee1054: amusement2213(965119573i32); + locket1059: slave2214 = 7760199733391528725u64; + cast1058: 12966888209334653896u64; + tank1057: Furniture1362 { + cent1369: pets890; + arm1366: pigs1701; + spark1363: rock1004; + fiction1370: weather1645; + sound1373: Connection750 { + surprise758: 9942577862111553981i64; + price756: plane2039; + skirt759: 7890693303010700262i64; + giraffe763: 4235370644u32; + punishment765: canvas433; + leg757: 18446744072264117414i32; + play762: hot868; + son766: Yoke743 { + school746: Fowl461 { + curve462: 61202u16; + existence463: tiger2080; + feet464: 31i8; + leg466: dress1612; + clocks467: Father456 { + whip458: argument524; + fifth457: 564703614545725657u64; + }; + net465: 17965u16; + }; + pail744: 622723946u32; + show745: 15446227364894017049u64; + }; + baby761: copy1689; + berry751: Doll1647 { + truck753: plane2039; + furniture754: 3793340394u32; + watch752: 18446744073334984656i32; + walk755: clouds1420; + }; + vegetable760: committee1698; + vase764: canvas433; + }; + beam1371: 196u8; + army1365: 18446744073709548299i16; + joke1372: 213u8; + carpenter1368: 1322606615u32; + clam1367: cause2079; + hall1364: sail1994; + }.army1365; +}.committee1054; +struct building2179 { + chicken2177: i8; + spiders2176: u64; + band2175; +} +struct twig2208 { + idea2202; + chicken2206; + rake2204: i8; + cakes2207: i16; + trade2203; + argument2205; + substance2201: u8; +} +function fuel2190(pig2200: u64) -> Control1590 Control1590 { + songs1591: help793; + development1593: { + distribution1709.lamp62; + tendency908; + { + woman1614; + twig2208 { + idea2202: Cub1830 { + plot1831: 422199564i32; + birth1832: burn1828; + }; + chicken2206: 3368473780u32; + rake2204: 18446744073709551556i8; + cakes2207: 7881i16; + trade2203: fact1172; + argument2205: 63454u16; + substance2201: competition1083; + } + }; + (10246059779620803615u64 + brass2040) + }; + winter1594: 59045u16; + taste1595: { + spiders2209 = Uncle122 { + turn126: yoke1280; + distribution123: 105i8; + station124: 4013279833u32; + fan129: 17836685868335826813u64; + snow125: 18446744073709551586i8; + orange127: 17897i16; + run128: 18446744073709539817i16; + coal130: void(); + }; + burn1828.songs1591; + void(); + void(); + spiders2209.coal130 + }; + grain1592: memory2210 = (18446744073709551603i8 / fact1172); +}; +function things2195(act2196: i32,gun2197: i64) -> i16 top2198 = egg2199 = 13994i16; +function pigs2191(mass2193: u16,flame2194: Tree348) -> i8 { + receipt792; + ((sneeze830 * 18446744073709541357i16) / things2195(376313183i32,1519377224858640973i64)) +}; +function knee2185(rod2186: u16,sleep2187: u64,office2188: u64,authority2189: i64) -> Control1590 Control1590 { + songs1591: void(); + development1593: receipt792; + winter1594: 65028u16; + taste1595: fuel2190(15380750445279860682u64).taste1595; + grain1592: pigs2191((woman1614 - 42855u16),sun2192 = Tree348 { + vessel349: pigs1701; + stage350: 14123246654183182973u64; + worm351: 28660u16; + chalk352: sail1994; + robin353: basket914; + }); +}; +function carpenter2174(scarf2180: struct {spiders2176: u64; +band2175: i64; +chicken2177: i8;},dinner2181: i64,breakfast2182: i32,waste2183: i16) -> Control1590 class2184 = knee2185(60650u16,9787427493806836780u64,13843555523552804670u64,dinner2181); +cabbage892 = { + place2178 = building2179 { + chicken2177: 2i8; + spiders2176: 4196368288344933175u64; + band2175: mask2124; + }; + carpenter2174(place2178,wall2007,18446744072467799749i32,sneeze830).development1593; + 18446744073709551526i8 +}; +function tree905(furniture2172: u16,ants2173: i8) -> Band315 team1741; +function mouth1417(chain2168: u16,learning2169: i16) -> void stone2170 = guide2171 = void(); +struct middle1425 { + walk755; + watch752: i32; + truck753; + furniture754; +} +function orange1451(beginner2167: i16) -> North85 North85 { + day87: quiver1858; + brake86: 242u8; + apple88: { + 161u8; + void(); + leg1497 + }; +}; +function hydrant2121(volleyball2165: u64) -> Ground732 Ground732 { + jeans703: (toad917 * sneeze830); + wing705: { + receipt792; + jam2111.temper1475 + }; + plane707: direction2166 = 18446744073709526807i16; + books706: ({ + lock1173; + 510002247558024757u64 + } + argument524.thread257); + meat704: 3289891242u32; +}; +function pipe2160(cellar2161: u16,gold2162: i16) -> Uncle122 son2163 = Uncle122 { + turn126: 18446744072066149598i32; + distribution123: brother834; + station124: goldfish856; + fan129: { + help793; + 185u8; + clouds1420; + chairs1873; + 295012521u32 + }; + snow125: join2164 = 65i8; + orange127: yoke1022; + run128: Note1190 { + unit1067: comparison1889; + bike1066: rabbits2045; + food1068: 18446744073709526343i16; + burst1065: eyes1242; + coil1064: 16493684089167360446i64; + }.food1068; + coal130: void(); +}; +dinner2122 = pipe2160(1105u16,22527i16); +function view2119(table2152: i64,curtain2153: Yoke743,writing2154: Ground732,chairs2155: i64,fall2156: u8,skirt2157: u8,brother2158: u16,love2159: i64) -> u32 (); +function rail2149() -> i8 learning2151 = 30i8; +function bun2145(hen2146: i8,crush2147: u8) -> Driving692 weight2148 = Driving692 { + boot697: 3853665709953443174i64; + cub695: rail2149(); + stop694: 245247691618486116u64; + geese693: canvas433; + pie698: -10359969805582698997i64; + action699: (sail1994 / 18446744072059163233i32); + box696: animal2150 = dress1612; + plants700: fear1395; +}; +locket2142 = Fork1583 { + mist1584: void(); + trees1585: babies2143 = (4841240764070299211i64 * 3736134426916443776i64); + servant1586: pencil2144 = bun2145(brass1421,121u8); +}.servant1586; +cracker2140 = locket2142; +function cent2139(writing2141: u64) -> u16 (); +function frame2123(wall2132: i64,example2133: i64,beginner2134: u64,knowledge2135: u64,eggnog2136: u16,foot2137: u8,voyage2138: u16) -> u16 cent2139((cracker2140.stop694 - { + 62554u16; + void(); + 18446744073709551517i8; + 82i8; + help793; + 875813533837147166u64; + void(); + 18446744073709545353i16 +})); +function tub2126(thought2127: u64,copy2128: u8,throne2129: u8,throne2130: u16,texture2131: u8) -> u8 committee1698; +acoustics1452 = hearing2118 = Furniture1362 { + cent1369: { + lock1173; + 6497498900448220430u64; + dress1612; + void(); + lock1173; + receipt792; + 938994005i32; + 112i8; + lock1173 + }.food1068; + arm1366: Profit1891 { + governor1894: copy1689; + insurance1892: 13955067698991991391i64; + surprise1893: secretary1857; + glass1895: sail1994; + sign1896: mailbox1664; + }.surprise1893; + spark1363: view2119(67i8,quiver2120 = jam982,hydrant2121(4895012138428441645u64),Ground237 { + baby241: 40803u16; + fly247: rock1004; + pin244: mitten2046; + join239: rock1004; + joke243: 17643070906494572833u64; + lace240: 18446744073709551514i8; + art245: art1743; + quartz238: 2026100939i32; + banana242: 4759560368308272720i64; + achieve246: support1980; + hands248: 16041347178947401528u64; + quilt249: cellar919; + transport250: dinner2122; + }.quilt249,20u8,lunch1696,59041u16,-trade2008); + fiction1370: frame2123(support1980,mask2124 = amusement887,stick1983.play762,8363773060594454321u64,{ + help793; + quiver1858 + },attack2125 = lunch1696,12024u16); + sound1373: jeans1281; + beam1371: 18u8; + army1365: sneeze830; + joke1372: tub2126(17150753450022888250u64,things1644,129u8,33972u16,committee1698); + carpenter1368: 3502045134u32; + clam1367: (); + hall1364: ((1449819814i32 + 18446744072675590622i32) + 18446744073709525828i16); +}; +function mom2115(eye2116: i32,night2117: i64) -> u64 (Banana1053 { + story1063: Note1190 { + unit1067: coal1020; + bike1066: anger596; + food1068: pigs1422; + burst1065: 80u8; + coil1064: start1419; + }; + amusement1055: secretary1857; + wrench1062: 18446744073565392206i32; + boats1071: 1054670067178976914u64; + action1072: 18446744073709521851i16; + reason1073: farmer1397; + vessel1069: brass2040; + driving1060: 53u8; + leather1061: measure1991; + waves1070: 123i8; + visitor1056: process1265; + committee1054: 1221523919i32; + locket1059: brass2040; + cast1058: 1290512449390357795u64; + tank1057: 18446744073709536475i16; +}.vessel1069 - 18200642458205253139u64); +function laborer1459(clouds2112: i64,wave2113: u16,women2114: i16) -> u64 mom2115(2121662478i32,(smell1854.surprise758 * 8383447168826181454i64)); +struct Test1472 { + plant1476: Note1190; + battle1473: u8; + rainstorm1474: u64; + temper1475: i16; +} +flowers1477 = jam2111 = tree1969.use1774.plant1476; +function meal2069(quilt2107: i32,donkey2108: Yoke743) -> Note1190 Note1190 { + unit1067: 118i8; + bike1066: (anger596 * fight2109 = anger596); + food1068: teeth2110 = straw1740; + burst1065: { + calendar522.pigs256; + void(); + void() + }; + coil1064: help793; +}; +struct Bath2102 { + measure2103: u16; + rake2104; + lake2105; +} +function brake2078(snakes2099: i32,smell2100: u8,swing2101: u64) -> Screw1951 { + void(); + start2106 = receipt792 +}.lake2105; +music2085 = { + { + (sneeze830 / quiver1858); + purpose2096 = void(); + void(); + { + Answer1090 { + sponge64: wall2007; + song57: tendency908; + breath56: wrist790; + stamp58: 7402928033883967404u64; + scarf59: 18446744073709548968i16; + trade63: help793; + downtown65: 55451u16; + change60: 111i8; + grip61: 18446744073709524259i16; + lamp62: void(); + nerve55: uncle2009; + }.lamp62; + { + weather1645; + 220u8 + }; + tiger2080; + print nerve1108; + (157u8 / 125u8) + }; + void() + }; + skin2097 = discovery2098 = receipt792; + print system992.hands248 +}; +struct stretch2088 { + nest2087: u16; +} +function system2093(insurance2094: u64) -> i16 { + { + lock1173; + lock1173; + brass1421; + grade1619 + }; + 89i8; + jelly2095 = measure1991 +}; +function comfort2086(sky2090: u32,snakes2091: u16,achieve2092: u8) -> u64 (system2093(12580830885355373420u64) * { + 18446744073709525736i16; + secretary1857; + Cable92 { + patch97: 18446744073521267419i32; + lock98: 18446744073709551514i8; + popcorn93: 6976501607877664281i64; + hydrant95: cellar919; + summer94: loss1695; + coast99: Judge45 { + dust50: 18446744073709549995i16; + pleasure69: 1769392122u32; + babies48: achieve2092; + icicle46: 18446744073709536751i16; + tray49: 191u8; + flavor70: 18446744073709551520i8; + stew71: process1265; + order47: 18446744073709551577i8; + flame51: 26002i16; + adjustment52: cats1547; + }; + cord96: 22865i16; + } +}); +income2032 = Animal1913 { + toys1903: ().joke1799; + boats1902: { + fear1395.coast99.icicle46; + void(); + meal2069(sail1994,Yoke743 { + school746: balls1437; + pail744: 2250199436u32; + show745: 15568620188336322107u64; + }).bike1066; + { + plane2077 = brake2078(1311533570i32,smash1096,mitten2046); + weather1645; + void(); + jump2070 = void(); + void(); + print pet1316; + { + jump2070; + 15077047038092070728i64; + 12195947967307977812u64; + lock1173; + help793 + }; + plane2077.spade1956 + }; + { + receipt792; + receipt792; + burn1828.songs1591; + { + dress1612; + void(); + receipt792; + help793; + surprise2041 + }; + coal1020; + 42i8 + } + }; + number1905: cause2079 = tiger2080 = 28462i16; + example1900: { + wool2084 = ocean2083; + note2082 = ocean2083 = wool2084; + afternoon2081 = lock1173; + note2082.coal130; + Ground237 { + baby241: 30599u16; + fly247: nail832; + pin244: brass2040; + join239: condition1744; + joke243: brass2040; + lace240: hammer1349; + art245: weather1645; + quartz238: 18446744072790851123i32; + banana242: 4513749804441404837i64; + achieve246: 15674170075012245739i64; + hands248: mitten2046; + quilt249: start1419; + transport250: ocean2083; + }.pin244 + }; + dime1909: music2085.wood1957; + playground1907: comfort2086({ + stretch2088 { + nest2087: 46942u16; + }; + 14332009389187156766i64; + breakfast889 + }.fly247,51u8,Spark1147 { + fruit1153: eyes1242; + muscle1149: 1233051113130596269u64; + church1151: yoke1280; + hate1154: condition1744; + apparatus1155: Deer969 { + calendar966: 18446744073709551530i8; + birthday964: pet1316; + pancake968: cub1979; + wind967: 527191318i32; + direction965: 7i8; + }; + sneeze1148: 6783655206630330906i64; + feather1152: tiger2080; + rake1150: 122u8; + }.fruit1153); + milk1904: 2200887631743701096i64; + weather1908: 32236i16; + monkey1906: (sticks2089 = committee1698 + 140u8); + horse1910: 18446744073709551553i8; + bread1901: 10602726619631685846u64; +}; +function bulb2064(ball2068: i64) -> Friction1287 { + 18446744072083831478i32; + help793; + jam982.school746.feet464; + void() +}; +function flower2061(lunch2065: i64,move2066: i16) -> Answer1090 Friction1287 { + page1289: wrist790; + fight1294: 43381u16; + crayon1291: yoke1280; + color1288: { + woman1614; + 18446744073709551559i8; + sail1994 + }; + hate1293: 28676u16; + observation1290: (loss1695 * 67u8); + flight1295: Friction1287 { + page1289: hammer1349; + fight1294: 4425199269462117852i64; + crayon1291: process1265; + color1288: 15304457536243299103i64; + hate1293: art1743; + observation1290: 160u8; + flight1295: distribution1709; + rabbit1292: 150u8; + }.flight1295; + rabbit1292: train2067 = lunch1696; +}.flight1295; +function day2034(place2057: i8,arch2058: u64,door2059: i32,beginner2060: u16) -> Answer1090 flower2061({ + receipt792; + Tree348 { + vessel349: 19301u16; + stage350: 17330302581387538358u64; + worm351: 38118u16; + chalk352: 1069592214i32; + robin353: basket914; + }.worm351; + void(); + 20818u16 +},{ + organization2063 = bulb2064(14104077308618489516i64); + disgust2062 = organization2063; + { + help793; + lock1173; + nail832 + }; + rock1004; + disgust2062.fight1294 +}); +function frogs2052() -> Song1795 Song1795 { + popcorn1796: prison882; + news1800: measure1804.news1800; + spy1797: 27610u16; + joke1799: (smell1860 * { + help793; + 1094625010318332078u64 + }); + owl1798: bath2050; +}; +function toothbrush2044(health2051: i16) -> Screw1951 Screw1951 { + spade1956: anger596; + basin1952: frogs2052(); + cork1959: fact1172; + sun1954: game2053 = (32u8 - 129u8); + reading1955: face2054 = whistle2055 = clouds1420; + card1953: geese2056 = { + 1308723842260052337u64; + 27257i16; + 161844642u32 + }; + wood1957: 18446744073709522288i16; + page1958: surprise2041; +}; +baby2038 = Friction1287 { + page1289: Note1190 { + unit1067: hammer1349; + bike1066: { + void(); + void() + }; + food1068: 1515i16; + burst1065: 157u8; + coil1064: (11131217784446628782i64 / trade2008); + }.unit1067; + fight1294: surprise2041 = loss1695; + crayon1291: { + (53622u16 * 54175u16); + void(); + nerve1108; + void() + }; + color1288: (start1419 - { + payment2042 = Bag1767 { + death1769: art1743; + quicksand1770: team1741; + death1768: 7599825262226470622i64; + }; + help793; + 46739u16; + nerve1108; + payment2042 + }); + hate1293: weather1645; + observation1290: Animal1913 { + toys1903: 2919828680u32; + boats1902: smell1860; + number1905: board2043 = 18446744073709540836i16; + example1900: 18240u16; + dime1909: smell1854.surprise758; + playground1907: { + 18446744072556344724i32; + woman1614; + 18446744073709551519i8; + void() + }; + milk1904: (increase1607 * 17674049707255382054i64); + weather1908: -28386i16; + monkey1906: { + void(); + 27u8 + }; + horse1910: mailbox1664.action1072; + bread1901: rock1004; + }.monkey1906; + flight1295: Answer1090 { + sponge64: toothbrush2044(quiver1175).reading1955; + song57: rabbits2045 = mitten2046 = 8092043937610347859u64; + breath56: 18446744073709551536i8; + stamp58: { + impulse2049 = Father456 { + whip458: Giraffe251 { + cart255: account1174; + coach253: 18446744072730078978i32; + company252: maid1746; + pigs256: clouds1420; + meeting254: sail1994; + thread257: 42337u16; + boys258: system992; + }; + fifth457: plane2039; + }; + stick2048 = impulse2049; + Animal1913 { + toys1903: brass2040; + boats1902: hot868; + number1905: pigs1422; + example1900: pigs1701; + dime1909: 15817046080587777757i64; + playground1907: 6277211329090722254u64; + milk1904: cellar919; + weather1908: 18446744073709548635i16; + monkey1906: 126u8; + horse1910: 30805i16; + bread1901: hot868; + }.bread1901; + Judge45 { + dust50: 18446744073709521371i16; + pleasure69: condition1744; + babies48: bomb1097; + icicle46: writer1399; + tray49: 82u8; + flavor70: coal1020; + stew71: process1265; + order47: 18446744073709551554i8; + flame51: uncle2009; + adjustment52: cats1547; + }.stew71; + void(); + sneeze2047 = plane2039; + Yoke743 { + school746: Fowl461 { + curve462: pet1316; + existence463: 18446744073709542176i16; + feet464: brass1421; + leg466: woman1614; + clocks467: stick2048; + net465: 6846u16; + }; + pail744: 934512702u32; + show745: tendency908; + } + }; + scarf59: { + lock1173; + 1578676565i32 + }; + trade63: void(); + downtown65: 32494u16; + change60: hammer1349; + grip61: 18446744073709530781i16; + lamp62: void(); + nerve55: 8876i16; + }; + rabbit1292: bath2050 = smash1096; +}; +writing1479 = Banana1053 { + story1063: cord2030 = Note1190 { + unit1067: 18446744073709551525i8; + bike1066: anger596; + food1068: measure1991; + burst1065: 111u8; + coil1064: support1980; + }; + amusement1055: 51u16; + wrench1062: 1950706883i32; + boats1071: { + print Furniture1362 { + cent1369: 3951i16; + arm1366: weather1645; + spark1363: rock1004; + fiction1370: pigs1701; + sound1373: stick1983; + beam1371: bomb1097; + army1365: pigs1422; + joke1372: 121u8; + carpenter1368: nail832; + clam1367: maid1746; + hall1364: 1779564440i32; + }.arm1366; + void(); + lock1173 + }; + action1072: vegetable2031 = income2032.number1905; + reason1073: body2033 = receipt792; + vessel1069: 15124093339434410253u64; + driving1060: Friction1287 { + page1289: 118i8; + fight1294: 12511536999283860500i64; + crayon1291: 108993636i32; + color1288: 9099366163870527069i64; + hate1293: { + lock1173; + salt1925; + 55844u16; + 1381212430i32; + lock1173 + }; + observation1290: (251u8 + 27u8); + flight1295: day2034(21i8,smell1860,yoke1280,36552u16); + rabbit1292: (bells1703 + things1644); + }.observation1290; + leather1061: eggs2035 = { + void(); + 64i8 + }; + waves1070: { + cloth2036 = 8991419794531201450i64; + 18446744073709531849i16; + { + void(); + void(); + smell1860 + } + }.calendar966; + visitor1056: payment2037 = baby2038.rabbit1292; + committee1054: (18446744071644495019i32 - 18446744072937380980i32); + locket1059: plane2039 = (brass2040 = 9819306851164872390u64 * tendency908); + cast1058: { + twig1739; + void(); + toad917 + }; + tank1057: uncle2009; +}; +function glue1478(twig2029: u64) -> u8 25u8; +struct mind1484 { + scissors313: i64; + observation311: u8; + crime314: u16; + development310: i8; + calculator312: u64; +} +function yarn1498(humor2027: i8,copy2028: u64) -> i8 -help793.use1774; +back2024 = { + void(); + bells1703; + void(); + void(); + self2025 = rail2026 = 8072834160540861586i64; + Answer1090 { + sponge64: 16452123543651519645i64; + song57: smell1860; + breath56: 4i8; + stamp58: 17334632810874831684u64; + scarf59: straw1740; + trade63: void(); + downtown65: 42391u16; + change60: 18446744073709551586i8; + grip61: maid1746; + lamp62: receipt792; + nerve55: pets890; + }.trade63 +}.cook2016; +bulb2023 = back2024; +hand1499 = bulb2023; +struct whip1510 { + robin1509: i64; + cart1508; + camp1507: u64; +} +pest1970 = tax2022 = Mint1788 { + holiday1793: pet1316; + driving1794: Detail1771 { + alley1783: Bag1767 { + death1769: 33701u16; + quicksand1770: team1741; + death1768: 15516353527225882083i64; + }; + building1776: condition1744; + space1775: 26772i16; + deer1777: wall2007; + ants1780: 32603i16; + cattle1781: 17761391312204502259i64; + trucks1772: hope1122; + government1779: 42221u16; + acoustics1782: wrist790; + egg1773: start1419; + growth1778: 4311019126000343877u64; + use1774: 18446744073709551496i8; + }; + animal1790: 33u8; + doll1791: 5050u16; + memory1789: 16437346645586529409u64; + quartz1792: nail832; +}.driving1794; +function paper2020(bell2021: u16) -> Detail1771 Mint1788 { + holiday1793: woman1614; + driving1794: tree1969; + animal1790: smile1691; + doll1791: finger1315; + memory1789: smell1860; + quartz1792: 3194946493u32; +}.driving1794; +dock1992 = paper2020((18333u16 / 40001u16)).alley1783; +function berry1993() -> Connection750 songs2019 = smell1854; +struct Holiday2012 { + cook2016: Spark1147; + bike2013: i16; + drum2014; + feet2015; +} +function carriage2006(peace2010: i64,pancake2011: i16) -> Spark1147 { + church2017 = void(); + juice2018 = sneeze830; + void() +}.cook2016; +galley2005 = carriage2006((wall2007 = 5485188608915843357i64 * trade2008 = { + void(); + 18446744073604908906i32; + help793; + smell1860 +}),uncle2009 = North85 { + day87: 1565192366i32; + brake86: maid1746; + apple88: 54605u16; +}.brake86); +function kettle1996(sand2002: u64,weight2003: i16) -> Deer969 distribution2004 = galley2005.apparatus1155; +function rate1999(wrist2000: i8,cake2001: u32) -> void increase1607; +function ticket1997(memory1998: u16) -> Answer1090 { + 3530203123u32; + rate1999(7i8,nail832); + process1265; + competition1083 +}; +breakfast1974 = Friction1287 { + page1289: Detail1771 { + alley1783: dock1992; + building1776: goldfish856; + space1775: 18446744073709529836i16; + deer1777: 12865767154834869349i64; + ants1780: experience1094; + cattle1781: support1980; + trucks1772: 18446744073709522743i16; + government1779: art1743; + acoustics1782: 18446744073709551508i8; + egg1773: 17812658760471652034i64; + growth1778: 17805012703181894091u64; + use1774: 18446744073709551516i8; + }.acoustics1782; + fight1294: berry1993().surprise758; + crayon1291: sail1994 = 305714275i32; + color1288: 5363980483584443167i64; + hate1293: ({ + help793; + void(); + print leg1497; + event1995 = lock1173 + } * { + 14916i16; + { + 18446744073709530209i16; + 119u8; + lock1173 + }; + 8597i16 + }); + observation1290: Spark1147 { + fruit1153: things1644; + muscle1149: { + void(); + 6810251917268914727i64; + Deer969 { + calendar966: comparison1889; + birthday964: 25646u16; + pancake968: 18446744072894053249i32; + wind967: sail1994; + direction965: fact1172; + }; + receipt792 + }; + church1151: Deer969 { + calendar966: 63i8; + birthday964: 36207u16; + pancake968: 741150961i32; + wind967: yoke1280; + direction965: 18446744073709551492i8; + }.wind967; + hate1154: (condition1744 + 3813122659u32); + apparatus1155: kettle1996(tendency908,toad917); + sneeze1148: 12501293603923014376i64; + feather1152: { + void(); + 16237u16; + finger1315 + }; + rake1150: 151u8; + }.fruit1153; + flight1295: ticket1997(12264u16); + rabbit1292: 84u8; +}; +balance1976 = Uncle122 { + turn126: 18446744072953159106i32; + distribution123: Answer1090 { + sponge64: copy1689; + song57: hot868; + breath56: comparison1889; + stamp58: 9954194280967334893u64; + scarf59: writer1399; + trade63: void(); + downtown65: 58087u16; + change60: hammer1349; + grip61: quiver1175; + lamp62: void(); + nerve55: writer1399; + }.breath56; + station124: { + receipt792; + 11640781973891278324i64; + receipt792 + }; + fan129: 15318514524771877630u64; + snow125: brother834; + orange127: measure1991 = tree1969.trucks1772; + run128: writer1399; + coal130: receipt792; +}; +function reading1977(birthday1989: u64,carpenter1990: u16) -> i64 10517147136964052398i64; +struct zoo1982 { + development310; + calculator312: u64; + crime314: i16; + observation311: u8; + scissors313: i64; +} +function light1981(elbow1985: i16,women1986: i8,passenger1987: u64,pets1988: u16) -> Fowl461 Fowl461 { + curve462: 28468u16; + existence463: help793; + feet464: 18446744073709551514i8; + leg466: committee1698; + clocks467: Fowl461 { + curve462: finger1315; + existence463: 18446744073709539780i16; + feet464: 18446744073709551580i8; + leg466: pets1988; + clocks467: Father456 { + whip458: edge521; + fifth457: tendency908; + }; + net465: pigs1701; + }.clocks467; + net465: Giraffe251 { + cart255: 2953i16; + coach253: cub1979; + company252: maid1746; + pigs256: breakfast889; + meeting254: 18446744072048721799i32; + thread257: 47780u16; + boys258: Ground237 { + baby241: 40613u16; + fly247: 2213157923u32; + pin244: smell1860; + join239: 3683130598u32; + joke243: 865933477447601167u64; + lace240: 110i8; + art245: 11346u16; + quartz238: 18446744072324528906i32; + banana242: 12739509957597596978i64; + achieve246: 1005579544201709432i64; + hands248: tendency908; + quilt249: amusement887; + transport250: Uncle122 { + turn126: process1265; + distribution123: 18446744073709551581i8; + station124: 484050869u32; + fan129: smell1860; + snow125: 56i8; + orange127: straw1740; + run128: pets890; + coal130: void(); + }; + }; + }.thread257; +}; +pan1978 = Friction1287 { + page1289: (); + fight1294: 5977187871556699537i64; + crayon1291: cub1979 = yoke1280; + color1288: (support1980 = -nerve1108 + 21023u16); + hate1293: light1981(Band315 { + answer322: zoo1982 { + development310: 18446744073709551541i8; + calculator312: anger596; + crime314: 18446744073709548980i16; + observation311: 56u8; + scissors313: breakfast889; + }; + crow316: 54626u16; + umbrella317: 17571480171255329672u64; + summer321: 12002178728992149072i64; + substance319: rock1004; + earthquake320: 19056i16; + exchange318: toad917; + }.exchange318,18446744073709551525i8,{ + secretary1857; + nerve1108 + },{ + void(); + 1648708923834149430u64 + }).curve462; + observation1290: stick1983 = smell1854.vegetable760; + flight1295: riddle1984 = Friction1287 { + page1289: 18446744073709551547i8; + fight1294: 13326152319140234203i64; + crayon1291: cub1979; + color1288: increase1607; + hate1293: 10711u16; + observation1290: bomb1097; + flight1295: distribution1709; + rabbit1292: 219u8; + }.flight1295; + rabbit1292: (print 42841u16 - 242u8); +}; +function office1968(war1971: i64,book1972: i64,fight1973: u16) -> Expert1681 Expert1681 { + payment1682: Expert1681 { + payment1682: hot868; + fiction1686: leg1497; + camp1683: canvas433; + brothers1684: tendency908; + leather1685: 12101857494862696588u64; + rail1687: breakfast1974; + }.leather1685; + fiction1686: { + void(); + 18446744073709551546i8; + help793; + Control1590 { + songs1591: void(); + development1593: void(); + winter1594: canvas433; + taste1595: hope1839; + grain1592: coal1020; + }.songs1591 + }; + camp1683: morning1975 = secretary1857; + brothers1684: balance1976.fan129; + leather1685: 8308979962713304789u64; + rail1687: Friction1287 { + page1289: system992.lace240; + fight1294: 52862u16; + crayon1291: yoke1280; + color1288: reading1977(18055628671965978815u64,pigs1701); + hate1293: 52172u16; + observation1290: 93u8; + flight1295: pan1978.flight1295; + rabbit1292: things1644; + }; +}; +cushion1967 = office1968(clouds1420,7823855378473273471i64,Mint1788 { + holiday1793: 25613u16; + driving1794: tree1969 = pest1970; + animal1790: 176u8; + doll1791: secretary1857; + memory1789: 1349330883u32; + quartz1792: help793; +}.holiday1793); +chicken1966 = cushion1967.rail1687; +function snow1513(push1961: i64,wealth1962: i32,feet1963: i64,afterthought1964: i64) -> Friction1287 control1965 = chicken1966; +struct Fork1583 { + mist1584: void; + trees1585: i64; + servant1586: Driving692; +} +struct Screw1951 { + spade1956: u64; + basin1952: Song1795; + cork1959: Profit1891; + sun1954: u8; + reading1955: i64; + card1953: i32; + wood1957: i64; + page1958: i64; +} +function cord1611(eye1948: i16,desk1949: i64,carpenter1950: i8) -> Banana1053 church1960 = Screw1951 { + spade1956: 6082263490828911824u64; + basin1952: amusement1803; + cork1959: Profit1891 { + governor1894: nerve1108; + insurance1892: 3249340508371211330i64; + surprise1893: finger1315; + glass1895: 18446744073267471714i32; + sign1896: Banana1053 { + story1063: Note1190 { + unit1067: 52i8; + bike1066: 15023741397357769951u64; + food1068: pets890; + burst1065: 141u8; + coil1064: chairs1873; + }; + amusement1055: 42851u16; + wrench1062: grade1640; + boats1071: smell1860; + action1072: account1174; + reason1073: North85 { + day87: 1726745706i32; + brake86: sneeze830; + apple88: 41123u16; + }; + vessel1069: 10154095664625072718u64; + driving1060: lunch1696; + leather1061: 18446744073709551318i16; + waves1070: coal1020; + visitor1056: 18446744071816735915i32; + committee1054: yoke1280; + locket1059: hot868; + cast1058: 13286654961258411881u64; + tank1057: quiver1175; + }; + }; + sun1954: competition1083; + reading1955: 12195782091682715145i64; + card1953: grade1640; + wood1957: clouds1420; + page1958: 17761308565753225555i64; +}.cork1959.sign1896; +function boy1942(ship1943: i16,lunchroom1944: u64) -> Answer1090 Answer1090 { + sponge64: 9208677320564975494i64; + song57: play894.boys258.joke243; + breath56: 18446744073709551496i8; + stamp58: snails1945 = garden1946 = smell1860; + scarf59: 1146i16; + trade63: print 18446744073709551517i8; + downtown65: canvas433; + change60: -{ + void(); + void(); + void(); + amusement887; + void() + }; + grip61: { + goldfish856; + help793 + }; + lamp62: { + spy1947 = Answer1090 { + sponge64: breakfast889; + song57: lunchroom1944; + breath56: 51i8; + stamp58: 13851831109797047284u64; + scarf59: 18446744073709542890i16; + trade63: help793; + downtown65: pigs1701; + change60: 71i8; + grip61: 18446744073709533044i16; + lamp62: help793; + nerve55: pigs1422; + }; + receipt792; + void(); + Skin1115 { + mother1121: Cats720 { + cannon724: yoke1280; + street721: 12101354692617917751i64; + fold725: Tree348 { + vessel349: woman1614; + stage350: 12739588205198599654u64; + worm351: art1743; + chalk352: yoke1280; + robin353: basket914; + }; + chance722: nail832; + cave723: rock1004; + }; + tax1117: Tree348 { + vessel349: 15997u16; + stage350: hot868; + worm351: 61656u16; + chalk352: 489443900i32; + robin353: Sand141 { + sister54: spy1947; + cart68: sneeze830; + van53: 62914u16; + guitar66: leg1497; + ghost67: cellar919; + }; + }; + oil1118: bells1095; + company1120: 206u16; + bubble1116: 11512i16; + badge1119: lunch1696; + }.badge1119; + void(); + { + lock1173; + void() + } + }; + nerve55: { + help793; + print 18343958871852682903u64; + pet1316 + }; +}; +cellar1941 = { + 18446744072852045249i32; + boy1942((105i16 - twig1739),tendency908).lamp62; + 11362481481852539548i64 +}; +goose1613 = Fork1583 { + mist1584: print anger596; + trees1585: chairs1873; + servant1586: cellar1941; +}; +function produce1928(trick1932: i16,party1933: i16,lace1934: u64,brother1935: u16) -> Doll1647 Doll1647 { + truck753: 13564299646217119181u64; + furniture754: nail832; + watch752: { + { + mist1938 = Driving692 { + boot697: nerve1108; + cub695: 65i8; + stop694: 17919914846094083707u64; + geese693: secretary1857; + pie698: amusement887; + action699: 18446744073604799535i32; + box696: pigs1701; + plants700: fear1395; + }; + tooth1937 = mist1938; + vein1936 = tooth1937; + dress1612; + void(); + void(); + Control1590 { + songs1591: receipt792; + development1593: lock1173; + winter1594: weather1645; + taste1595: Fork1583 { + mist1584: help793; + trees1585: 6377441782572382161i64; + servant1586: vein1936; + }; + grain1592: yoke1022; + } + }; + 51490u16; + sponge1939 = lock1173; + receipt792; + void() + }; + walk755: neck1940 = { + lace1934; + experience1094; + void() + }; +}; +function earthquake1926(twist1929: u32) -> struct {watch752: i32; +truck753: u64; +walk755: i64; +furniture754: u32;} Doll1647 { + truck753: { + 739173648076001976u64; + receipt792; + Control1590 { + songs1591: void(); + development1593: void(); + winter1594: pet1316; + taste1595: hope1839; + grain1592: coal1020; + }.development1593; + receipt792; + receipt792; + lock1173 + }; + furniture754: { + bells1095; + match1930 = void(); + 178u8 + }; + watch752: crate1931 = 1274622538i32; + walk755: 9251820987310236943i64; +}; +stitch1920 = earthquake1926(porter1927 = produce1928(twig1739,writer1399,1614903190830679365u64,art1743).furniture754); +rain1924 = salt1925 = { + hope1839.mist1584; + void() +}.driving1794; +function clock1615(airplane1914: u16,distance1915: u16,lettuce1916: i8,band1917: i8) -> Connection750 Connection750 { + surprise758: 9605466105258000288i64; + price756: 3566736109331847963u64; + skirt759: -3698504396948230714i64; + giraffe763: goldfish856; + punishment765: 50081u16; + leg757: band1918 = lock1114.cannon724; + play762: ink1919 = Connection750 { + surprise758: 16414847696994922030i64; + price756: tendency908; + skirt759: nerve1108; + giraffe763: 1313388229u32; + punishment765: airplane1914; + leg757: quiver1858; + play762: 13994350057369543675u64; + son766: Yoke743 { + school746: cannon1305; + pail744: nail832; + show745: 16106250867303452451u64; + }; + baby761: thumb888; + berry751: stitch1920; + vegetable760: bells1703; + vase764: finger1315; + }.play762; + son766: Yoke743 { + school746: { + art1743; + loss1695 + }; + pail744: chin1921 = nail832; + show745: lettuce1922 = 4320157581717568151u64; + }; + baby761: { + (); + 18446744073709539634i16; + copy1689 + }; + berry751: curve1923 = (); + vegetable760: Mint1788 { + holiday1793: 63822u16; + driving1794: rain1924; + animal1790: bells1095; + doll1791: 4362u16; + memory1789: lettuce1922; + quartz1792: goldfish856; + }.animal1790; + vase764: 4677u16; +}; +struct agreement1881 { + board1878; + police1879: u32; +} +struct wood1886 { + farmer1882: i64; + good-bye1885; + caption1883: i64; + hat1884: u64; +} +struct Profit1891 { + governor1894; + insurance1892: i64; + surprise1893; + glass1895; + sign1896: Banana1053; +} +struct Animal1913 { + toys1903: u64; + boats1902; + number1905: i16; + example1900: u16; + dime1909: i64; + playground1907: u64; + milk1904: i64; + weather1908: i16; + monkey1906: u8; + horse1910; + bread1901: u64; +} +authority1855 = Uncle122 { + turn126: ({ + Answer1090 { + sponge64: 5800253695054825481i64; + song57: 1858065219044841934u64; + breath56: 18446744073709551499i8; + stamp58: hot868; + scarf59: sneeze830; + trade63: void(); + downtown65: 6654u16; + change60: coal1020; + grip61: 3107i16; + lamp62: void(); + nerve55: hope1122; + }.lamp62; + cast1877 = void(); + { + respect1880 = agreement1881 { + board1878: twig1739; + police1879: goldfish856; + }; + finger1315; + void(); + respect1880; + void() + }; + { + ornament1887 = Furniture1362 { + cent1369: 11393i16; + arm1366: 9167u16; + spark1363: 2572112121u32; + fiction1370: 12826u16; + sound1373: jeans1281; + beam1371: 49u8; + army1365: 18446744073709543483i16; + joke1372: bells1703; + carpenter1368: 2873384018u32; + clam1367: sneeze830; + hall1364: 18446744072008661975i32; + }; + wood1886 { + farmer1882: amusement887; + good-bye1885: hot868; + caption1883: 10155842167576022728i64; + hat1884: 13287451672532810941u64; + }; + secretary1857; + ornament1887 + }; + bat1888 = cast1877; + void() + } + 18446744073709535202i16); + distribution123: comparison1889 = 86i8; + station124: { + secretary1857; + void(); + riddle1890 = smell1854.vase764; + void(); + void(); + { + business1897 = hair1898 = Profit1891 { + governor1894: chairs1873; + insurance1892: start1419; + surprise1893: 10025u16; + glass1895: 18446744073671454394i32; + sign1896: Banana1053 { + story1063: care1398; + amusement1055: secretary1857; + wrench1062: yoke1280; + boats1071: 2702259734556877269u64; + action1072: experience1094; + reason1073: farmer1397; + vessel1069: 16924226516428125527u64; + driving1060: 115u8; + leather1061: 18446744073709543794i16; + waves1070: brother834; + visitor1056: 397193335i32; + committee1054: grade1640; + locket1059: 10083997662857367899u64; + cast1058: anger596; + tank1057: hope1122; + }; + }; + distribution1709.lamp62; + business1897.sign1896 + }; + void() + }; + fan129: { + { + 12990679481133787708i64; + lock1173 + }; + 867340703i32; + void(); + crush1899 = (); + void(); + { + crook1912 = Animal1913 { + toys1903: 7598562172892584103u64; + boats1902: hot868; + number1905: hope1122; + example1900: 25202u16; + dime1909: increase1607; + playground1907: 4100348095344098448u64; + milk1904: 9593234710637322458i64; + weather1908: sneeze830; + monkey1906: grade1619; + horse1910: 18446744073709542346i16; + bread1901: hot868; + }; + print secretary1857; + religion1911 = crook1912; + print crush1899 + } + }; + snow125: 110i8; + orange127: 18446744073709535119i16; + run128: quiver1175; + coal130: void(); +}; +function match1856(thought1875: u16) -> u64 (3272229326985198347u64 * chance1876 = { + bomb1097; + 12040807156938872983i64; + void(); + help793; + 18446744073709519562i16 +}); +function ray1872(arithmetic1874: u8) -> i64 (brother834 * (116i8 + yoke1022)); +wind1869 = Doll1647 { + truck753: ({ + turn1870 = art1743; + account1174; + drain1871 = void(); + void(); + drain1871 + } * 3469264794617802592u64); + furniture754: goldfish856; + watch752: { + ray1872(236u8); + distribution1709.lamp62; + rock1004 + }.day87; + walk755: chairs1873 = 2092482474u32; +}; +function skate1859(print1866: u16,hand1867: u64) -> Furniture1362 robin1868 = Furniture1362 { + cent1369: 18446744073709519081i16; + arm1366: 20346u16; + spark1363: 1285213032u32; + fiction1370: 30722u16; + sound1373: Connection750 { + surprise758: start1419; + price756: 1350287325462412397u64; + skirt759: breakfast889; + giraffe763: rock1004; + punishment765: woman1614; + leg757: quiver1858; + play762: tendency908; + son766: jam982; + baby761: 7692172171359375706i64; + berry751: wind1869; + vegetable760: 242u8; + vase764: weather1645; + }; + beam1371: 23u8; + army1365: 18446744073709535187i16; + joke1372: 136u8; + carpenter1368: 3664391330u32; + clam1367: 12231i16; + hall1364: quiver1858; +}; +function texture1862(harmony1865: u16) -> i16 experience1094; +pan1618 = Banana1053 { + story1063: mailbox1664.story1063; + amusement1055: (smell1854 = Connection750 { + surprise758: thumb888; + price756: 16767205564796689407u64; + skirt759: nerve1108; + giraffe763: 2813831602u32; + punishment765: 55013u16; + leg757: 121106456i32; + play762: 14842867169596810686u64; + son766: Yoke743 { + school746: Fowl461 { + curve462: weather1645; + existence463: 25598i16; + feet464: yoke1022; + leg466: 16136u16; + clocks467: Father456 { + whip458: Giraffe251 { + cart255: account1174; + coach253: yoke1280; + company252: 18446744073709532178i16; + pigs256: 8320015425081106908i64; + meeting254: process1265; + thread257: pigs1701; + boys258: Ground237 { + baby241: weather1645; + fly247: goldfish856; + pin244: 6449450591722332425u64; + join239: 995152924u32; + joke243: tendency908; + lace240: 18446744073709551492i8; + art245: 16875u16; + quartz238: process1265; + banana242: clouds1420; + achieve246: 1527467681993082347i64; + hands248: 3929874097172415023u64; + quilt249: copy1689; + transport250: authority1855; + }; + }; + fifth457: tendency908; + }; + net465: canvas433; + }; + pail744: nail832; + show745: 564949550920251072u64; + }; + baby761: amusement887; + berry751: Doll1647 { + truck753: 5123865650505546403u64; + furniture754: nail832; + watch752: 1184504548i32; + walk755: amusement887; + }; + vegetable760: 156u8; + vase764: finger1315; + }.punishment765 * 11u8); + wrench1062: (yoke1280 + 18446744073205887989i32); + boats1071: match1856(secretary1857 = (weather1645 - art1743)); + action1072: account1174; + reason1073: North85 { + day87: quiver1858 = process1265; + brake86: skate1859(canvas433,hot868).cent1369; + apple88: grade1619; + }; + vessel1069: smell1860 = { + 18446744071627725163i32; + void() + }; + driving1060: things1644; + leather1061: experience1094; + waves1070: ({ + anger596; + 3873637513u32; + void(); + lock1173 + } / 32i8); + visitor1056: { + { + 1320u16; + dress1612; + coal1020; + dirt1861 = lock1173 + }; + (18446744073709531771i16 / 23574i16); + { + texture1862(40794u16); + { + 2606u16; + clouds1420; + nerve1108; + canvas433; + hammer1349 + }; + void(); + void() + }; + salt1863 = Note1190 { + unit1067: coal1020; + bike1066: anger596; + food1068: 18446744073709544867i16; + burst1065: bomb1097; + coil1064: 9393035500076038191i64; + }.bike1066; + brass1421 + }; + committee1054: 18446744072473090916i32; + locket1059: { + void(); + 18446744073709550662i16; + stream1864 = receipt792 + }; + cast1058: Yoke743 { + school746: Fowl461 { + curve462: 51942u16; + existence463: 18446744073709530131i16; + feet464: wrist790; + leg466: secretary1857; + clocks467: Father456 { + whip458: argument524; + fifth457: smell1860; + }; + net465: 44109u16; + }; + pail744: 2075617091u32; + show745: hot868; + }.show745; + tank1057: 3048i16; +}; +struct pleasure1627 { + observation311: u8; + calculator312: u64; + crime314; + scissors313: i64; + development310; +} +function garden1837(spy1853: i32) -> Driving692 { + void(); + void(); + void(); + print 22487u16 +}; +struct Cub1830 { + plot1831: i32; + birth1832: Control1590; +} +function temper1843(amount1847: u8,scissors1848: i64,birth1849: u64,bears1850: u8,judge1851: i32,grandfather1852: u8) -> void void(); +function songs1842(whip1844: i16,eggnog1845: i64,basin1846: u64) -> Fork1583 hope1839; +function pancake1840(calendar1841: i16) -> Fork1583 songs1842({ + temper1843(bean1697,nerve1108,17898146594817014138u64,smile1691,18446744073157231781i32,160u8); + -amusement887; + void() +},12080083756044196085i64,anger596); +brush1838 = hope1839 = pancake1840(6036i16); +head1827 = burn1828 = Control1590 { + songs1591: void(); + development1593: void(); + winter1594: { + fight1836 = garden1837(grade1640); + rhythm1835 = Fork1583 { + mist1584: lock1173; + trees1585: 1963976915693401096i64; + servant1586: fight1836; + }; + collar1833 = representative1834 = Cub1830 { + plot1831: 1326018870i32; + birth1832: Control1590 { + songs1591: void(); + development1593: lock1173; + winter1594: 9088u16; + taste1595: rhythm1835; + grain1592: 64i8; + }; + }; + bite1829 = collar1833.birth1832; + bite1829.songs1591 + }; + taste1595: brush1838; + grain1592: 57i8; +}; +function boundary1642(force1825: u16,office1826: i16) -> Control1590 head1827; +function history1822(calculator1823: i8,price1824: u64) -> Ground237 icicle831; +function steel1819(ghost1820: i16) -> Ground237 bee1821 = history1822(yoke1022,pigs1701); +function cemetery1643(engine1816: i16,stage1817: i32,walk1818: u8) -> Ground237 steel1819(13914i16); +struct Doll1647 { + truck753; + furniture754; + watch752; + walk755; +} +addition1657 = watch1814 = way1815 = 18446744073709551520i8.plant1476; +function money1810(trick1811: u8,smile1812: i8) -> Manager1041 stone1813 = Manager1041 { + building1043: legs1809; + pocket1042: 30666i16; +}; +plot1764 = legs1809 = money1810(Spark1147 { + fruit1153: competition1083; + muscle1149: hot868; + church1151: 18446744072929674717i32; + hate1154: rock1004; + apparatus1155: Deer969 { + calendar966: brother834; + birthday964: canvas433; + pancake968: grade1640; + wind967: yoke1280; + direction965: wrist790; + }; + sneeze1148: 5191943562356054990i64; + feather1152: hope1122; + rake1150: bells1095; +}.fruit1153,18446744073709551542i8).building1043; +struct Bag1767 { + death1769: u16; + quicksand1770; + death1768: i64; +} +struct Detail1771 { + alley1783: Bag1767; + building1776: u32; + space1775: i16; + deer1777: i64; + ants1780; + cattle1781: i64; + trucks1772: i16; + government1779: u16; + acoustics1782: i8; + egg1773; + growth1778: u64; + use1774: i8; +} +struct Mint1788 { + holiday1793: u16; + driving1794: Detail1771; + animal1790; + doll1791; + memory1789: u64; + quartz1792: u32; +} +struct Song1795 { + popcorn1796: Tree348; + news1800: Mint1788; + spy1797: u16; + joke1799: u64; + owl1798; +} +expansion1805 = { + money1808 = floor1807; + children1806 = floor1807 = money1808; + 3259019684309088478i64; + help793; + 49649u16; + (children1806.unit1067 / 18446744073709551591i8) +}; +chin1802 = amusement1803 = measure1804 = expansion1805; +throat1801 = chin1802; +function form1784(distance1785: i64,net1786: u64) -> Detail1771 sneeze1787 = throat1801.news1800.driving1794; +function maid1763(week1765: u64,way1766: i16) -> Band315 form1784(13017035695829621169i64,18361871624524337241u64).alley1783.quicksand1770; +vase1742 = maid1763(183u8,Manager1041 { + building1043: plot1764; + pocket1042: toad917; +}.building1043.plane707); +function magic1759(fear1760: i32) -> void { + help793; + 10475551271216606093i64; + popcorn1761 = Answer1090 { + sponge64: 751691166016882510i64; + song57: hot868; + breath56: wrist790; + stamp58: 7913502475214420855u64; + scarf59: 20034i16; + trade63: receipt792; + downtown65: 5356u16; + change60: 18446744073709551503i8; + grip61: hope1122; + lamp62: void(); + nerve55: 18446744073709535916i16; + }.trade63; + toad1762 = 514144285u32; + (bomb1097 * lunch1696); + print twig1739 +}; +function mice1751(drink1756: u64,things1757: u64,goose1758: u8) -> void magic1759({ + canvas433; + 18446744073709538636i16; + 143953300i32 +}); +function sand1753(bucket1754: i16,cap1755: i32) -> Driving692 { + { + lock1173; + 82u8 + }; + lock1173; + help793 +}.servant1586; +bell1747 = { + 18446744073709532823i16; + { + 4u8; + 18446744073709551590i8; + library1748 = 51348u16; + 18446744071838981203i32; + hammer1349; + agreement1749 = void(); + prison882.chalk352; + view1750 = void(); + { + feeling1752 = sand1753(toad917,795602083i32); + view1750; + mice1751(anger596,anger596,27u8); + Control1590 { + songs1591: lock1173; + development1593: void(); + winter1594: 26072u16; + taste1595: Fork1583 { + mist1584: receipt792; + trees1585: breakfast889; + servant1586: feeling1752; + }; + grain1592: 18446744073709551588i8; + }.development1593 + } + }; + 13618235690291016891u64; + void() +}; +farmer1745 = bell1747; +day1668 = Band315 { + answer322: team1741 = vase1742.answer322; + crow316: art1743 = dress1612; + umbrella317: 8862134881357143070u64; + summer321: copy1689; + substance319: condition1744 = { + void(); + void(); + weather1645 + }; + earthquake320: farmer1745.wing705; + exchange318: -maid1746 = 18446744073709546372i16; +}; +struct frogs1738 { + level1735: i16; + butter1731: u64; + show1730: Ground732; + achiever1729: u16; + dock1734: i16; + transport1733; + cap1732: i32; + fifth1736: u16; + dog1737: i32; +} +coach1721 = Test1472 { + plant1476: Test1472 { + plant1476: Note1190 { + unit1067: fact1172; + bike1066: tendency908; + food1068: account1174; + burst1065: 202u8; + coil1064: increase1607; + }; + battle1473: 194u8; + rainstorm1474: hot868; + temper1475: distribution1709.nerve55; + }.plant1476; + battle1473: 12u8; + rainstorm1474: { + Furniture1362 { + cent1369: 18446744073709531391i16; + arm1366: canvas433; + spark1363: 3569998639u32; + fiction1370: 47053u16; + sound1373: jeans1281; + beam1371: 85u8; + army1365: 18446744073709547493i16; + joke1372: 220u8; + carpenter1368: 1468168713u32; + clam1367: 18446744073709537954i16; + hall1364: 18446744072384820735i32; + }.beam1371; + soda1726 = Ground237 { + baby241: woman1614; + fly247: leg1497; + pin244: tendency908; + join239: 1099881331u32; + joke243: 14203753421793388377u64; + lace240: hammer1349; + art245: canvas433; + quartz238: 1057803600i32; + banana242: amusement887; + achieve246: amusement887; + hands248: 17561751173844319725u64; + quilt249: 15237065709272286333i64; + transport250: Uncle122 { + turn126: 507337992i32; + distribution123: coal1020; + station124: 755315434u32; + fan129: tendency908; + snow125: hammer1349; + orange127: 18446744073709533696i16; + run128: 18446744073709534664i16; + coal130: lock1173; + }; + }.pin244; + transport1727 = help793; + { + part1728 = void(); + void() + }; + lock1173; + 376688849i32; + frogs1738 { + level1735: 18446744073709533914i16; + butter1731: 2416338954u32; + show1730: Ground732 { + jeans703: toad917; + wing705: 17i8; + plane707: 18446744073709526799i16; + books706: pet1316; + meat704: 2216592220u32; + }; + achiever1729: (pigs1701 / 32644u16); + dock1734: 5i8; + transport1733: things1644; + cap1732: grade1640; + fifth1736: cannon1305.net465; + dog1737: Furniture1362 { + cent1369: quiver1175; + arm1366: weather1645; + spark1363: nail832; + fiction1370: 51962u16; + sound1373: Connection750 { + surprise758: amusement887; + price756: 7509775126073119283u64; + skirt759: 1017416117956092911i64; + giraffe763: 267394507u32; + punishment765: 47090u16; + leg757: grade1640; + play762: 6004614908424402981u64; + son766: jam982; + baby761: 1840887199434489685i64; + berry751: Doll1647 { + truck753: soda1726; + furniture754: 1122192524u32; + watch752: grade1640; + walk755: clouds1420; + }; + vegetable760: grade1619; + vase764: 42333u16; + }; + beam1371: committee1698; + army1365: 22708i16; + joke1372: grade1619; + carpenter1368: goldfish856; + clam1367: 18446744073709549313i16; + hall1364: yoke1280; + }.hall1364; + } + }; + temper1475: twig1739 = straw1740 = pan1679.bubble1116; +}; +function rat1720(mitten1722: i64,pin1723: u64,planes1724: i64) -> Skin1115 { + letter1725 = (1665284863i32 * yoke1280); + print 2675681473u32 +}; +function crack1716(dolls1717: i16,kitty1718: u8) -> Skin1115 pies1719 = rat1720(finger1315,coach1721.rainstorm1474,8103227221486841887i64); +function glove1680(badge1714: u32) -> Skin1115 swim1715 = crack1716(18446744073709533175i16,(things1644 - 51u8)); +struct Expert1681 { + payment1682: u64; + fiction1686: u32; + camp1683: u16; + brothers1684: u64; + leather1685; + rail1687: Friction1287; +} +function respect1700(harmony1711: u16,popcorn1712: i16,able1713: u32) -> Banana1053 mailbox1664; +example1708 = distribution1709 = { + goose1710 = hot868; + lock1173 +}.flight1295; +function army1705(sponge1706: u16,airplane1707: i8) -> Answer1090 example1708; +function request1702(rail1704: i16) -> Answer1090 army1705(9464u16,-{ + 11796u16; + void(); + void(); + void() +}); +caption1688 = Friction1287 { + page1289: (respect1700(42255u16,account1174,3551403904u32).waves1070 * 18446744073709551524i8); + fight1294: { + void(); + void(); + hot868; + void(); + 2996389446053808552i64 + }; + crayon1291: sneeze830; + color1288: -40464u16; + hate1293: pigs1701 = finger1315; + observation1290: grade1619; + flight1295: request1702(14456i16); + rabbit1292: ((40u8 / things1644) / (bells1703 = 175u8 + 152u8)); +}; +function guide1699() -> Answer1090 help793; +geese1690 = guide1699(); +door1677 = Skin1115 { + mother1121: pan1679 = glove1680(3501556391u32).mother1121; + tax1117: Tree348 { + vessel349: (22706u16 / 14918u16); + stage350: anger596; + worm351: 46211u16; + chalk352: 799043837i32; + robin353: Sand141 { + sister54: Answer1090 { + sponge64: 8850625187603936759i64; + song57: 7554681524395509949u64; + breath56: 91i8; + stamp58: 6840138755660393897u64; + scarf59: 28804i16; + trade63: lock1173; + downtown65: finger1315; + change60: yoke1022; + grip61: hope1122; + lamp62: lock1173; + nerve55: 18446744073709542575i16; + }; + cart68: bomb1097; + van53: (); + guitar66: finger1315; + ghost67: (17063143761058663685i64 - 17672869823810753395i64); + }; + }; + oil1118: Expert1681 { + payment1682: hot868; + fiction1686: 255694020u32; + camp1683: finger1315; + brothers1684: anger596; + leather1685: 1557424213146737308u64; + rail1687: caption1688; + }.rail1687.observation1290; + company1120: Friction1287 { + page1289: -83i8; + fight1294: copy1689 = 2722568134282392441i64; + crayon1291: 18446744073093955034i32; + color1288: nerve1108; + hate1293: 56755u16; + observation1290: 149u8; + flight1295: geese1690; + rabbit1292: smile1691 = bells1095; + }.hate1293; + bubble1116: { + celery1692 = void(); + achieve1693 = space1694 = 18446744073508871167i32; + () + }; + badge1119: (loss1695 = lunch1696 = 150u8 + bean1697 = committee1698 = 151u8); +}; +bomb1678 = Ground237 { + baby241: 13243u16; + fly247: leg1497; + pin244: hot868; + join239: 1578500180u32; + joke243: 17526084211491618582u64; + lace240: 18446744073709551527i8; + art245: pet1316; + quartz238: yoke1280; + banana242: cellar919; + achieve246: start1419; + hands248: tendency908; + quilt249: breakfast889; + transport250: Uncle122 { + turn126: grade1640; + distribution123: coal1020; + station124: 3225802030u32; + fan129: tendency908; + snow125: wrist790; + orange127: 29904i16; + run128: pets890; + coal130: lock1173; + }; +}.quartz238; +function zephyr1667(frogs1669: i64,motion1670: i64) -> Banana1053 Banana1053 { + story1063: Note1190 { + unit1067: brother834; + bike1066: things1644; + food1068: 26356i16; + burst1065: { + 8129335920343574610i64; + void() + }; + coil1064: { + 7527018604137741066u64; + help793; + anger596 + }; + }; + amusement1055: (1215u16 * 53216u16); + wrench1062: 18446744071877059200i32; + boats1071: tendency908; + action1072: void(); + reason1073: { + weather1645; + void(); + quiver1175; + void(); + void(); + void() + }; + vessel1069: flock1671 = rock1004; + driving1060: ({ + lock1173; + help793; + help793 + } - crowd1672 = 231u8); + leather1061: { + cats1675 = Driving692 { + boot697: motion1670; + cub695: 62i8; + stop694: tendency908; + geese693: 19042u16; + pie698: amusement887; + action699: 500512524i32; + box696: 62971u16; + plants700: Cable92 { + patch97: process1265; + lock98: 18446744073709551528i8; + popcorn93: 4914463847157657879i64; + hydrant95: 6019449467451313928i64; + summer94: 202u8; + coast99: Judge45 { + dust50: hope1122; + pleasure69: 4021239744u32; + babies48: smash1096; + icicle46: 4000i16; + tray49: bomb1097; + flavor70: brass1421; + stew71: process1265; + order47: brother834; + flame51: toad917; + adjustment52: Sand141 { + sister54: Answer1090 { + sponge64: increase1607; + song57: anger596; + breath56: 18446744073709551499i8; + stamp58: tendency908; + scarf59: pigs1422; + trade63: lock1173; + downtown65: 14714u16; + change60: brass1421; + grip61: 17654i16; + lamp62: lock1173; + nerve55: 18446744073709545969i16; + }; + cart68: experience1094; + van53: weather1645; + guitar66: leg1497; + ghost67: 5948300455477574494i64; + }; + }; + cord96: hope1122; + }; + }; + skate1674 = cats1675; + square1673 = Fork1583 { + mist1584: void(); + trees1585: motion1670; + servant1586: skate1674; + }; + Control1590 { + songs1591: receipt792; + development1593: receipt792; + winter1594: pet1316; + taste1595: square1673; + grain1592: brass1421; + }.songs1591 + }; + waves1070: brass1421; + visitor1056: knee1676 = { + yoke1022; + receipt792; + void(); + void(); + rock1004; + void(); + help793 + }; + committee1054: door1677.mother1121.cannon724; + locket1059: 7409730878779684990u64; + cast1058: (dress1612 - 17315620512855731184u64); + tank1057: (bomb1678.grip61 - (pigs1422 - 15119i16)); +}; +function cork1665(appliance1666: i8) -> Banana1053 zephyr1667(-day1668.summer321,grade1640); +kitten1663 = mailbox1664 = cork1665(4i8); +toothpaste1658 = kitten1663; +function duck1659(skate1662: i64) -> Answer1090 22831i16.flight1295; +function laborer1651(recess1655: u8) -> Answer1090 Answer1090 { + sponge64: language1656 = edge521.pigs256; + song57: 14025931123815884533u64; + breath56: (fact1172 + addition1657.unit1067); + stamp58: toothpaste1658.driving1060; + scarf59: yoke1022; + trade63: duck1659(cellar919).trade63; + downtown65: temper1660 = 42204u16; + change60: 18446744073709551577i8; + grip61: { + 12690154288765545402u64; + 18446744073709522448i16; + 1404528906350910944u64 + }.leather1061; + lamp62: lock1173; + nerve55: fork1661 = sneeze830; +}; +function spoon1652(cry1654: i64) -> i64 Driving692 { + boot697: amusement887; + cub695: 18446744073709551533i8; + stop694: 10093905337168802719u64; + geese693: 28801u16; + pie698: cellar919; + action699: grade1640; + box696: 63296u16; + plants700: Cable92 { + patch97: grade1640; + lock98: hammer1349; + popcorn93: 126604871892096176i64; + hydrant95: cellar919; + summer94: 74u8; + coast99: Judge45 { + dust50: 18446744073709521221i16; + pleasure69: 398083840u32; + babies48: 229u8; + icicle46: sneeze830; + tray49: bells1095; + flavor70: 18446744073709551610i8; + stew71: 297819347i32; + order47: 18446744073709551553i8; + flame51: 31966i16; + adjustment52: cats1547; + }; + cord96: 18446744073709531779i16; + }; +}.boot697; +ocean1639 = Answer1090 { + sponge64: grade1640 = process1265; + song57: { + oatmeal1641 = boundary1642(1039u16,18446744073709534577i16); + oatmeal1641.songs1591 + }; + breath56: fact1172; + stamp58: cemetery1643(hope1122,process1265,things1644 = 38u8).hands248; + scarf59: -{ + void(); + fear1395.patch97; + 7265i16 + }; + trade63: void(); + downtown65: weather1645 = 56202u16; + change60: 18446744073709551524i8; + grip61: { + music1650 = Friction1287 { + page1289: brass1421; + fight1294: cellar919; + crayon1291: Banana1053 { + story1063: Note1190 { + unit1067: 18446744073709551534i8; + bike1066: 15781122079557771559u64; + food1068: quiver1175; + burst1065: eyes1242; + coil1064: clouds1420; + }; + amusement1055: canvas433; + wrench1062: 18446744071589829589i32; + boats1071: 919016105915434035u64; + action1072: 18446744073709532334i16; + reason1073: farmer1397; + vessel1069: tendency908; + driving1060: 181u8; + leather1061: hope1122; + waves1070: 18446744073709551609i8; + visitor1056: grade1640; + committee1054: 18446744072409396674i32; + locket1059: 945621433293981831u64; + cast1058: 3197457254495707483u64; + tank1057: sneeze830; + }.visitor1056; + color1288: Sand141 { + sister54: Answer1090 { + sponge64: 10492828389873328203i64; + song57: hot868; + breath56: 50i8; + stamp58: 6927584836630670360u64; + scarf59: account1174; + trade63: void(); + downtown65: weather1645; + change60: 18446744073709551598i8; + grip61: hope1122; + lamp62: lock1173; + nerve55: 18446744073709536904i16; + }; + cart68: writer1399; + van53: 21188u16; + guitar66: rock1004; + ghost67: nerve1108; + }.ghost67; + hate1293: dress1612; + observation1290: 97u8; + flight1295: Answer1090 { + sponge64: increase1607; + song57: 4953594291958433937u64; + breath56: coal1020; + stamp58: 2757027847662563119u64; + scarf59: writer1399; + trade63: lock1173; + downtown65: 27255u16; + change60: brass1421; + grip61: 18446744073709532947i16; + lamp62: help793; + nerve55: 18446744073709539581i16; + }; + rabbit1292: { + pet1316; + lock1173; + 14653i16 + }; + }; + cannon1649 = Doll1647 { + truck753: 15388281373399683117u64; + furniture754: 1372214u32; + watch752: yoke1280; + walk755: music1650.color1288; + }; + truck1646 = bath1648 = cannon1649; + Answer1090 { + sponge64: breakfast889; + song57: hot868; + breath56: coal1020; + stamp58: 15107212965990381079u64; + scarf59: 18446744073709546268i16; + trade63: help793; + downtown65: pet1316; + change60: 18446744073709551532i8; + grip61: 21145i16; + lamp62: help793; + nerve55: 3574i16; + }.lamp62; + void(); + (smash1096 / eyes1242); + (things1644 * Connection750 { + surprise758: 16797172234701563704i64; + price756: tendency908; + skirt759: 6102757322679152885i64; + giraffe763: leg1497; + punishment765: 10915u16; + leg757: process1265; + play762: anger596; + son766: Yoke743 { + school746: balls1437; + pail744: rock1004; + show745: 6101880039703515662u64; + }; + baby761: cellar919; + berry751: truck1646; + vegetable760: 15u8; + vase764: 56958u16; + }.vegetable760) + }; + lamp62: { + { + 4064908845275031218u64; + void(); + brother834 + }; + laborer1651(competition1083).trade63; + receipt792 + }; + nerve55: { + -jeans1281.baby761; + spoon1652(hook1653 = 61077368166887679i64); + void() + }; +}; +function blow1636(prose1637: u16) -> Answer1090 step1638 = ocean1639; +servant1628 = blow1636(24622u16); +struct bike1631 { + observation311: u8; + crime314: u8; + development310: i8; + calculator312: u64; + scissors313: i64; +} +chickens1634 = { + 16312067244701521770u64; + lawyer1635 = lock1173 +}; +advice1632 = chickens1634; +function friction1629(digestion1630: i64) -> u32 Band315 { + answer322: Band315 { + answer322: bike1631 { + observation311: 125u8; + crime314: eyes1242; + development310: fact1172; + calculator312: 17795372354291267934u64; + scissors313: amusement887; + }; + crow316: woman1614; + umbrella317: tendency908; + summer321: 6607828933501001552i64; + substance319: 811127332u32; + earthquake320: 18446744073709549077i16; + exchange318: hope1122; + }.answer322; + crow316: { + dress1612; + void(); + help793 + }; + umbrella317: (tendency908 - 16050333139354915074u64); + summer321: icicle831.quilt249; + substance319: nail832; + earthquake320: Judge45 { + dust50: pets890; + pleasure69: leg1497; + babies48: competition1083; + icicle46: toad917; + tray49: grade1619; + flavor70: 59i8; + stew71: process1265; + order47: coal1020; + flame51: toad917; + adjustment52: Sand141 { + sister54: advice1632; + cart68: pigs1422; + van53: 17980u16; + guitar66: 263735151u32; + ghost67: 471631691367282291i64; + }; + }.icicle46; + exchange318: skin1633 = account1174; +}.substance319; +function cakes1617(creator1620: i16,hose1621: Banana1053,experience1622: u32,system1623: i16,clocks1624: i32,screw1625: i8,frog1626: i32) -> Band315 Band315 { + answer322: pleasure1627 { + observation311: bomb1097; + calculator312: 14936469626378372329u64; + crime314: servant1628; + scissors313: start1419; + development310: 58i8; + }; + crow316: eyes1242; + umbrella317: { + void(); + lock1173 + }; + summer321: 4027396743778257270i64; + substance319: friction1629(breakfast889); + earthquake320: 21863i16; + exchange318: 11198i16; +}; +name1610 = Furniture1362 { + cent1369: cord1611(pigs1422,5124159397642726246i64,18446744073709551562i8).leather1061; + arm1366: dress1612 = (Control1590 { + songs1591: void(); + development1593: void(); + winter1594: canvas433; + taste1595: goose1613; + grain1592: brother834; + }.winter1594 + 46337u16); + spark1363: leg1497; + fiction1370: woman1614 = dress1612; + sound1373: clock1615(8224u16,15560266579318560525u64,18446744073709551540i8,-Answer1090 { + sponge64: breakfast889; + song57: 3327901077433208642u64; + breath56: 74i8; + stamp58: 2806325052265521127u64; + scarf59: 7016i16; + trade63: help793; + downtown65: woman1614; + change60: coal1020; + grip61: account1174; + lamp62: void(); + nerve55: 18446744073709524147i16; + }.change60); + beam1371: competition1083; + army1365: (); + joke1372: ({ + void(); + brush1616 = receipt792 + } - bells1095); + carpenter1368: (cakes1617(writer1399,pan1618,nail832,experience1094,yoke1280,18446744073709551587i8,18446744072577127046i32).substance319 - ()); + clam1367: grade1619 = 114u8; + hall1364: 18446744073357193208i32; +}; +function comb1602(cow1608: i8,pancake1609: i64) -> Furniture1362 name1610; +quiver1588 = comb1602({ + juice1603 = (7546876549710733336u64 * anger596); + cakes1604 = void(); + touch1605 = cover1606 = help793 +},(increase1607 = 3644632898909843744i64 * bomb1097)); +struct Control1590 { + songs1591: void; + development1593: void; + winter1594: u16; + taste1595: Fork1583; + grain1592: i8; +} +function cow1597(mint1598: u16,title1599: u16) -> Fork1583 { + argument1600 = (tendency908 * tendency908); + basket1601 = print process1265; + (1616101276990580949i64 / breakfast889) +}; +function pan1587(sock1589: i32) -> Fork1583 Control1590 { + songs1591: { + 20104u16; + help793 + }; + development1593: lock1173; + winter1594: soap1596 = 44774u16; + taste1595: cow1597(canvas433,40970u16); + grain1592: wrist790; +}.taste1595; +function pot1523(size1580: u16,scene1581: i8,error1582: void) -> Driving692 pan1587(quiver1588.hall1364).servant1586; +struct governor1571 { + point1566: u64; + owl1567: u8; + able1565: u32; + songs1564; + food1568: u64; + drop1570: i64; + pickle1569; +} +struct paper1578 { + bed1575; + trousers1576: i32; +} +function field1563(mint1572: struct {point1566: u64; +pickle1569: u32; +owl1567: u8; +drop1570: _; +songs1564: u64; +able1565: u32; +food1568: _;}) -> Cable92 friction1573 = Cable92 { + patch97: 291433072i32; + lock98: 109i8; + popcorn93: lizards1574 = 4642001051788681587i64; + hydrant95: 6734185463575301034i64; + summer94: { + position1577 = paper1578 { + bed1575: 4293812711682933546u64; + trousers1576: 18446744072560182829i32; + }; + help793; + lock1173; + position1577 + }; + coast99: fear1395.coast99; + cord96: throne1579 = 20600i16; +}; +function bath1531(sneeze1560: i16,ocean1561: u16) -> i64 street1562 = field1563(governor1571 { + point1566: 4255139588104427877u64; + owl1567: 108u8; + able1565: rock1004; + songs1564: anger596; + food1568: 16175310899227613880u64; + drop1570: cellar919; + pickle1569: 3559831882u32; +}).hydrant95; +function committee1533() -> u8 competition1083; +struct adjustment1559 { + liquid1556: u8; + bite1557; + rice1555: u64; +} +function bridge1551(money1552: u64,humor1553: i16,glue1554: i64) -> Judge45 Cable92 { + patch97: 18446744071671589943i32; + lock98: 18446744073709551590i8; + popcorn93: nerve1108; + hydrant95: nail832; + summer94: 149u8; + coast99: fear1395.coast99; + cord96: { + wilderness1558 = adjustment1559 { + liquid1556: bells1095; + bite1557: 18446744073709526242i16; + rice1555: 16626992814878485121u64; + }; + lock1173; + wilderness1558; + help793 + }; +}.coast99; +function effect1549(flowers1550: u64) -> Judge45 bridge1551(14174088420529504992u64,pigs1422,2105769730218998601i64); +mass1548 = effect1549(tendency908); +cent1546 = cats1547 = mass1548.adjustment52; +jar1545 = cent1546; +balls1544 = jar1545; +function school1538(calendar1539: u8,sticks1540: u64,quiet1541: i16,stitch1542: i64) -> Judge45 brother1543 = Cable92 { + patch97: yoke1280; + lock98: yoke1022; + popcorn93: 200124458548783540i64; + hydrant95: clouds1420; + summer94: bells1095; + coast99: Judge45 { + dust50: 18446744073709547785i16; + pleasure69: 3104111968u32; + babies48: 210u8; + icicle46: account1174; + tray49: 195u8; + flavor70: yoke1022; + stew71: process1265; + order47: yoke1022; + flame51: pigs1422; + adjustment52: balls1544; + }; + cord96: experience1094; +}.coast99; +function patch1535(feast1537: u64) -> Judge45 school1538(eyes1242,smash1096,quiver1175,(13143069535288220518i64 - 1770455044435845240i64)); +structure1534 = patch1535(boundary1536 = void()); +function structure1520(field1521: i16) -> Driving692 Driving692 { + boot697: start1419; + cub695: { + lock1173; + punishment1522 = help793 + }; + stop694: hot868; + geese693: pot1523(pet1316,82i8,receipt792).box696; + pie698: chess1524 = hospital1525 = breakfast889; + action699: yoke1280; + box696: { + nerve1529 = { + void(); + lock1173; + yoke1280; + void() + }; + hose1527 = van1528 = nerve1529; + lock1173; + pull1526 = anger596; + 1354908722u32; + hose1527.burst1065 + }; + plants700: Cable92 { + patch97: crowd1530 = 18446744071950954157i32; + lock98: 18446744073709551524i8; + popcorn93: bath1531(18446744073709519571i16,pet1316); + hydrant95: basket1532 = 6240463712551566512i64; + summer94: committee1533(); + coast99: Cable92 { + patch97: 18446744073254569090i32; + lock98: wrist790; + popcorn93: basket1532; + hydrant95: cellar919; + summer94: 176u8; + coast99: structure1534; + cord96: 14244i16; + }.coast99; + cord96: 6691i16; + }; +}; +function ticket1514(car1515: i64,rings1516: u64,mass1517: u8,book1518: i32,fly1519: i16) -> Driving692 structure1520(((quiver1175 * hope1122) / { + help793; + help793; + void(); + void(); + receipt792 +})); +stem1505 = Driving692 { + boot697: { + yard1506 = Uncle122 { + turn126: yoke1280; + distribution123: 18446744073709551613i8; + station124: leg1497; + fan129: 18167233073927945369u64; + snow125: 84i8; + orange127: hope1122; + run128: 31040i16; + coal130: receipt792; + }; + yard1506.coal130 + }.boot697; + cub695: { + whip1510 { + robin1509: { + receipt792; + hope1122; + pets890; + void() + }; + cart1508: (3032052528u32 / 2398054038u32); + camp1507: 4571737743890378064u64; + }; + print void(); + ants1511 = lock1173 + }; + stop694: 3525087166953870408u64; + geese693: { + 18446744073431669913i32; + banana1512 = void(); + snow1513(amusement887,yoke1280,13131009161760722963i64,2283414868220850817i64).fight1294 + }; + pie698: 8633390498152339211i64; + action699: 465173075i32; + box696: pet1316; + plants700: ticket1514(8898055043654071373i64,3175592210225814569u64,{ + thumb888; + sneeze830; + nail832 + },{ + 8546i16; + void() + },play894.cart255).plants700; +}; +function quince1500(cream1501: i8,discussion1502: u32,spy1503: u8,rose1504: u64) -> Judge45 stem1505.plants700.coast99; +needle1485 = Judge45 { + dust50: Friction1287 { + page1289: yoke1022; + fight1294: 2757902619681620832i64; + crayon1291: yoke1280; + color1288: 4281555461248805616i64; + hate1293: 47794u16; + observation1290: eyes1242; + flight1295: Answer1090 { + sponge64: 13590034336938143330i64; + song57: tendency908; + breath56: 123i8; + stamp58: hot868; + scarf59: writer1399; + trade63: help793; + downtown65: finger1315; + change60: 86i8; + grip61: 18446744073709535739i16; + lamp62: void(); + nerve55: account1174; + }; + rabbit1292: 240u8; + }.flight1295.scarf59; + pleasure69: leg1497 = rock1004; + babies48: { + { + finger1315; + 1718173555i32 + }; + 6140i16; + competition1083 + }; + icicle46: yoke1022; + tray49: 203u8; + flavor70: yarn1498((126i8 - 65i8),leg1497); + stew71: hand1499.church1151; + order47: quince1500(brass1421,635860689u32,210u8,anger596).flavor70; + flame51: Answer1090 { + sponge64: 4266265441697989703i64; + song57: 7015149873756434142u64; + breath56: coal1020; + stamp58: 12665108066686891010u64; + scarf59: 18446744073709519204i16; + trade63: receipt792; + downtown65: finger1315; + change60: fact1172; + grip61: pigs1422; + lamp62: void(); + nerve55: 18446744073709523079i16; + }.grip61; + adjustment52: fear1395.coast99.adjustment52; +}.adjustment52; +function scissors1488(tramp1495: i64,anger1496: u16) -> i64 2154375179u32; +function transport1487(linen1489: i64,truck1490: u16,verse1491: i64) -> Answer1090 Answer1090 { + sponge64: cushion1492 = 6831947864831202846i64; + song57: rhythm1493 = 14516852947103463241u64; + breath56: 92i8; + stamp58: thunder1494 = hot868; + scarf59: { + tendency908; + start1419; + brother834; + 18446744073709551531i8; + brass1421 + }; + trade63: void(); + downtown65: finger1315; + change60: (80i8 + 18446744073709551533i8); + grip61: pets890; + lamp62: help793; + nerve55: 6980i16; +}; +function dress1483() -> Friction1287 Friction1287 { + page1289: { + 11396u16; + 98i8 + }; + fight1294: Band315 { + answer322: mind1484 { + scissors313: 12920349614856036599i64; + observation311: bells1095; + crime314: pet1316; + development310: yoke1022; + calculator312: hot868; + }; + crow316: finger1315; + umbrella317: anger596; + summer321: clouds1420; + substance319: nail832; + earthquake320: 18446744073709529082i16; + exchange318: 21635i16; + }.summer321; + crayon1291: yoke1280; + color1288: thumb888; + hate1293: Cable92 { + patch97: 18446744073006462177i32; + lock98: 18446744073709551574i8; + popcorn93: 5666817603710612467i64; + hydrant95: thumb888; + summer94: smash1096; + coast99: Judge45 { + dust50: 18077i16; + pleasure69: 2910324877u32; + babies48: bomb1097; + icicle46: account1174; + tray49: eyes1242; + flavor70: 71i8; + stew71: 18446744072442816320i32; + order47: wrist790; + flame51: sneeze830; + adjustment52: needle1485; + }; + cord96: 18446744073709526028i16; + }.summer94; + observation1290: { + pipe1486 = Uncle122 { + turn126: 1928318414i32; + distribution123: brother834; + station124: 490131817u32; + fan129: 11625473458915040304u64; + snow125: hammer1349; + orange127: 21305i16; + run128: 2837i16; + coal130: lock1173; + }; + pipe1486.distribution123 + }; + flight1295: transport1487((),47789u16,scissors1488(start1419,pet1316)); + rabbit1292: 10u8; +}; +function mitten1471(pocket1480: i16,business1481: Note1190,beam1482: u8) -> Answer1090 dress1483().flight1295; +function frame1465(need1467: i64,wax1468: u8,base1469: u64,act1470: i16) -> Answer1090 mitten1471(quiver1175,Test1472 { + plant1476: flowers1477; + battle1473: 122u8; + rainstorm1474: 14560901770682732127u64; + temper1475: 18446744073709520866i16; +}.plant1476,glue1478(writing1479.cast1058)); +function potato1463(competition1464: i16) -> Answer1090 frame1465(cellar919,(106u8 * { + 140u8; + 2915417113952731845u64 +}),9322009901451087147u64,scarf1466 = 51u8); +butter1462 = potato1463(18446744073709542185i16); +function hole1458(hearing1461: u64) -> Answer1090 Friction1287 { + page1289: 13i8; + fight1294: 2053488904410858754i64; + crayon1291: yoke1280; + color1288: clouds1420; + hate1293: 13389u16; + observation1290: eyes1242; + flight1295: butter1462; + rabbit1292: bomb1097; +}.flight1295; +function slave1456(pan1457: i64) -> Answer1090 hole1458(laborer1459({ + 7144852149901951432u64; + clouds1420; + hope1122; + void(); + tendency908 +},boats1460 = 61157u16,quiver1175)); +flowers1455 = slave1456(cellar919); +function sister1434(minister1446: i64,distribution1447: i32,sidewalk1448: i64) -> Banana1053 Banana1053 { + story1063: star1449 = Banana1053 { + story1063: care1398; + amusement1055: finger1315; + wrench1062: 2103114776i32; + boats1071: 15563178514494757045u64; + action1072: 18446744073709546696i16; + reason1073: North85 { + day87: process1265; + brake86: account1174; + apple88: 47271u16; + }; + vessel1069: anger596; + driving1060: 70u8; + leather1061: 5580i16; + waves1070: fact1172; + visitor1056: 747499603i32; + committee1054: 18446744072995607435i32; + locket1059: 13670158958589329008u64; + cast1058: hot868; + tank1057: quiver1175; + }.story1063; + amusement1055: 5651u16; + wrench1062: blow1450 = -18446744072950077352i32; + boats1071: 17611458761262852494u64; + action1072: sneeze830; + reason1073: orange1451(-18446744073709544344i16); + vessel1069: acoustics1452.sound1373.price756; + driving1060: { + receipt792; + 16751821483699814089i64; + coach1453 = 18446744073709518976i16 + }; + leather1061: 81u8; + waves1070: (); + visitor1056: { + void(); + 6187009708023852189i64 + }; + committee1054: 869007713i32; + locket1059: { + (tendency908 / tendency908); + void(); + { + 4596338795255538155u64; + process1265; + void(); + 110i8 + }; + void(); + birth1454 = receipt792; + birth1454 + }; + cast1058: 12315920649454523991u64; + tank1057: flowers1455.grip61; +}; +aunt1438 = { + mass1441 = 7502659381008768053i64; + void(); + Answer1090 { + sponge64: fear1395.popcorn93; + song57: system1442 = hot868; + breath56: tomatoes1443 = hammer1349; + stamp58: anger596; + scarf59: song1444 = 18446744073709547846i16; + trade63: void(); + downtown65: 40458u16; + change60: fear1445 = 116i8; + grip61: (writer1399 - 18446744073709533470i16); + lamp62: void(); + nerve55: 12754i16; + }.song57 +}; +function smoke1436(light1439: i8,moon1440: u16) -> i16 18446744073709545962i16; +thing1418 = Answer1090 { + sponge64: start1419 = clouds1420 = calendar522.pigs256; + song57: 1251712487670444829u64; + breath56: brass1421 = 18446744073709551595i8; + stamp58: 2261527996u32; + scarf59: pigs1422 = (Banana1053 { + story1063: Note1190 { + unit1067: 19i8; + bike1066: tendency908; + food1068: 18446744073709525790i16; + burst1065: 212u8; + coil1064: 12945515809907560087i64; + }; + amusement1055: 44284u16; + wrench1062: yoke1280; + boats1071: 4921349460765275198u64; + action1072: 18446744073709522716i16; + reason1073: North85 { + day87: yoke1280; + brake86: 27784i16; + apple88: 38237u16; + }; + vessel1069: hot868; + driving1060: 198u8; + leather1061: quiver1175; + waves1070: yoke1022; + visitor1056: 18446744073296728234i32; + committee1054: process1265; + locket1059: 13288807942825423315u64; + cast1058: anger596; + tank1057: toad917; + }.leather1061 - 19256i16); + trade63: void(); + downtown65: { + bike1428 = paint1429 = Ground732 { + jeans703: 18446744073709550542i16; + wing705: 96i8; + plane707: 18446744073709523748i16; + books706: 1062u16; + meat704: 796613507u32; + }; + nut1426 = Father456 { + whip458: Father456 { + whip458: edge521; + fifth457: hot868; + }.whip458; + fifth457: anger596; + }; + pump1424 = nut1426; + 18446744073709551511i8; + fiction1423 = Furniture1362 { + cent1369: 18446744073709526879i16; + arm1366: 62792u16; + spark1363: 4064096121u32; + fiction1370: 29422u16; + sound1373: Connection750 { + surprise758: clouds1420; + price756: hot868; + skirt759: cellar919; + giraffe763: rock1004; + punishment765: 38699u16; + leg757: process1265; + play762: hot868; + son766: Yoke743 { + school746: Fowl461 { + curve462: canvas433; + existence463: 29764i16; + feet464: 37i8; + leg466: 50453u16; + clocks467: pump1424; + net465: 56971u16; + }; + pail744: 2620677759u32; + show745: hot868; + }; + baby761: cellar919; + berry751: middle1425 { + walk755: 5925378196866493536i64; + watch752: 18446744073307095448i32; + truck753: tendency908; + furniture754: 2017951010u32; + }; + vegetable760: 97u8; + vase764: pet1316; + }; + beam1371: 95u8; + army1365: account1174; + joke1372: 49u8; + carpenter1368: 1331338141u32; + clam1367: toad917; + hall1364: 18446744073252672482i32; + }.spark1363; + help793; + pot1427 = bike1428.plane707 + }; + change60: { + 63i8; + { + snakes1433 = sister1434(clouds1420,18446744072436604310i32,11684290868380803949i64); + news1431 = plants1432 = snakes1433; + zinc1430 = tendency908; + news1431.driving1060 + }; + 29810i16; + ocean1435 = void(); + lock1173; + void() + }; + grip61: smoke1436(brother834,balls1437 = Fowl461 { + curve462: canvas433; + existence463: pigs1422; + feet464: brass1421; + leg466: 44309u16; + clocks467: Father456 { + whip458: Giraffe251 { + cart255: 14909i16; + coach253: 1752526656i32; + company252: account1174; + pigs256: 16498773455007072248i64; + meeting254: 18446744072603431249i32; + thread257: finger1315; + boys258: Ground237 { + baby241: 3605u16; + fly247: nail832; + pin244: tendency908; + join239: goldfish856; + joke243: hot868; + lace240: 89i8; + art245: finger1315; + quartz238: 18446744072520025605i32; + banana242: nerve1108; + achieve246: 17937412644821272155i64; + hands248: hot868; + quilt249: 10657859532568408924i64; + transport250: aunt1438; + }; + }; + fifth457: anger596; + }; + net465: 33005u16; + }.net465); + lamp62: void(); + nerve55: -(pigs1422 * { + start1419; + void(); + lock1173; + void(); + lock1173 + }); +}; +beggar907 = Tree348 { + vessel349: ((18194u16 / 24889u16) - (65u16 - 9079u16)); + stage350: mouth1417(canvas433,10496i16); + worm351: 14527u16; + chalk352: (yoke1280 * 18446744073460968759i32); + robin353: Sand141 { + sister54: thing1418; + cart68: 22045i16; + van53: 45732u16; + guitar66: bells1095; + ghost67: beds1221.street721; + }; +}.chalk352; +struct clouds915 { + change60: i64; + stamp58: i8; + breath56: u16; + song57: u64; + trade63: void; + scarf59: i16; + grip61; + sponge64: u8; + downtown65: u16; + nerve55: i16; + lamp62: void; +} +struct Banana1053 { + story1063; + amusement1055: u16; + wrench1062: i32; + boats1071: u64; + action1072: i16; + reason1073; + vessel1069: u64; + driving1060: u8; + leather1061: i16; + waves1070; + visitor1056: i32; + committee1054: i32; + locket1059: u64; + cast1058: u64; + tank1057: i16; +} +function order1409() -> void void(); +function ball1413(passenger1414: u64) -> Uncle122 Uncle122 { + turn126: 18446744073350150964i32; + distribution123: hammer1349; + station124: 51287u16; + fan129: 9906822156596903421u64; + snow125: { + thing1416 = Banana1053 { + story1063: Note1190 { + unit1067: 18446744073709551613i8; + bike1066: hot868; + food1068: 18446744073709528406i16; + burst1065: smash1096; + coil1064: thumb888; + }; + amusement1055: finger1315; + wrench1062: yoke1280; + boats1071: hot868; + action1072: sneeze830; + reason1073: farmer1397; + vessel1069: passenger1414; + driving1060: 180u8; + leather1061: 20018i16; + waves1070: 18446744073709551586i8; + visitor1056: 1228812784i32; + committee1054: process1265; + locket1059: 12741210541131058500u64; + cast1058: passenger1414; + tank1057: writer1399; + }; + day1415 = thing1416; + fact1172; + day1415 + }; + orange127: 18446744073709539324i16; + run128: 7342i16; + coal130: { + competition1083; + void() + }; +}; +function mouth1411() -> Uncle122 drug1412 = ball1413((tendency908 + hot868)); +function gate1400(dinosaurs1401: u32,crime1402: i64,title1403: u16,badge1404: i64,self1405: u16,map1406: i64,sense1407: u8,money1408: u32) -> Connection750 { + country1410 = mouth1411(); + order1409(); + 6811u16; + country1410.coal130; + void(); + void() +}; +boys1074 = care1398 = Note1190 { + unit1067: 18446744073709551504i8; + bike1066: hot868; + food1068: writer1399 = Ground732 { + jeans703: pets890; + wing705: hammer1349; + plane707: sneeze830; + books706: 1642u16; + meat704: nail832; + }.jeans703; + burst1065: 136u8; + coil1064: gate1400(rock1004,cellar919,39038u16,cellar919,60775u16,7774683471645189745i64,bells1095,goldfish856).baby761; +}; +cable1075 = farmer1397 = { + smash1096; + receipt792; + help793 +}.reason1073; +selection1076 = yoke1280; +power1077 = fear1395 = { + { + (); + jellyfish1396 = help793; + 8497788296942319328u64; + jellyfish1396 + }; + 10344885182598945662u64; + Skin1115 { + mother1121: lock1114; + tax1117: prison882; + oil1118: 104u8; + company1120: 63418u16; + bubble1116: 18446744073709521983i16; + badge1119: smash1096; + }.tax1117.worm351 +}; +yarn1176 = 976534468i32; +monkey1222 = (30u8 / bomb1097); +struct cushion1358 { + observation311; + scissors313: i64; + development310: i8; + calculator312: u64; + crime314: u64; +} +function clam1393(egg1394: i16) -> i8 18446744073709551530i8; +function word1360(chain1391: u16,cheese1392: i64) -> i8 clam1393((competition1083 / 18446744073709533738i16)); +struct Furniture1362 { + cent1369; + arm1366: u16; + spark1363: u32; + fiction1370: u16; + sound1373: Connection750; + beam1371: u8; + army1365: i16; + joke1372: u8; + carpenter1368: u32; + clam1367: i16; + hall1364: i32; +} +struct polish1374 { + watch752: i32; + walk755: i64; + furniture754: u32; + truck753: u64; +} +function passenger1378(wire1390: u16) -> Tree348 Tree348 { + vessel349: 45264u16; + stage350: (); + worm351: 29715u16; + chalk352: wire1390; + robin353: basket914; +}; +function sack1385(watch1386: u64,calendar1387: u64) -> void decision1388 = shade1389 = lock1173; +function burst1382(property1383: u8,window1384: i32) -> void sack1385(17378439407123089875u64,Uncle122 { + turn126: yoke1280; + distribution123: 18446744073709551510i8; + station124: rock1004; + fan129: 10277135495790528484u64; + snow125: yoke1022; + orange127: experience1094; + run128: sneeze830; + coal130: void(); +}.fan129); +function pipe1379(aftermath1380: void) -> u32 { + judge1381 = 15578236142847061417i64; + burst1382(eyes1242,18446744072400076886i32) +}.pail744; +function doctor1376(mint1377: u64) -> Skin1115 Skin1115 { + mother1121: Cats720 { + cannon724: lock1114.cannon724; + street721: 3040229945182072982i64; + fold725: passenger1378(pet1316); + chance722: pipe1379(void()); + cave723: 4213765677u32; + }; + tax1117: (); + oil1118: 121u8; + company1120: 47314u16; + bubble1116: 19286i16; + badge1119: 107u8; +}; +function apparatus1346(touch1357: u8) -> Band315 Band315 { + answer322: cushion1358 { + observation311: cars1359 = smash1096; + scissors313: 11051165419163349116i64; + development310: word1360(121u16,cellar919); + calculator312: wealth1361 = 1039183103455720047u64; + crime314: (wealth1361 * anger596); + }; + crow316: { + { + anger596; + breakfast889 + }; + Judge45 { + dust50: 30298i16; + pleasure69: 1161384774u32; + babies48: bells1095; + icicle46: account1174; + tray49: bells1095; + flavor70: coal1020; + stew71: process1265; + order47: 18446744073709551517i8; + flame51: toad917; + adjustment52: Sand141 { + sister54: Answer1090 { + sponge64: thumb888; + song57: 11902519011580371273u64; + breath56: hammer1349; + stamp58: 12000692066618227412u64; + scarf59: hope1122; + trade63: receipt792; + downtown65: pet1316; + change60: yoke1022; + grip61: experience1094; + lamp62: void(); + nerve55: sneeze830; + }; + cart68: 18446744073709519538i16; + van53: 21724u16; + guitar66: 2173094668u32; + ghost67: 5600912113644010622i64; + }; + }.stew71; + void(); + 16708133203326142296u64; + lock1173 + }; + umbrella317: 372164266u32; + summer321: Furniture1362 { + cent1369: 18446744073709541255i16; + arm1366: 47939u16; + spark1363: goldfish856; + fiction1370: finger1315; + sound1373: Connection750 { + surprise758: 709578169035141379i64; + price756: 1273898944410753237u64; + skirt759: 11440499144410861431i64; + giraffe763: goldfish856; + punishment765: pet1316; + leg757: 931004623i32; + play762: hot868; + son766: Yoke743 { + school746: work1306; + pail744: 1578604141u32; + show745: anger596; + }; + baby761: 3190619890886123160i64; + berry751: polish1374 { + watch752: process1265; + walk755: nerve1108; + furniture754: rock1004; + truck753: 10702674991698918603u64; + }; + vegetable760: 13u8; + vase764: 36896u16; + }; + beam1371: 22u8; + army1365: 18446744073709546428i16; + joke1372: 9u8; + carpenter1368: goldfish856; + clam1367: pets890; + hall1364: 18446744071912197472i32; + }.sound1373.skirt759; + substance319: pet1316; + earthquake320: clock1375 = pets890; + exchange318: doctor1376(wealth1361).bubble1116; +}; +function shoe1353(crowd1355: i64,comfort1356: u64) -> i16 14781i16; +hands1223 = Answer1090 { + sponge64: { + { + fire1345 = apparatus1346(203u8); + mint1344 = lock1173; + fire1345.umbrella317 + }; + bomb1347 = help793; + plastic1348 = void() + }; + song57: 5039936477968731010u64; + breath56: hammer1349 = Uncle122 { + turn126: process1265; + distribution123: coal1020; + station124: rock1004; + fan129: tendency908; + snow125: 18446744073709551580i8; + orange127: 7232i16; + run128: 18446744073709526522i16; + coal130: void(); + }.distribution123; + stamp58: { + canvas433; + { + (250u8 - 235u8); + hall1350 = receipt792; + void(); + void(); + help793 + }; + goose1351 = 52807u16; + thrill1352 = receipt792 + }; + scarf59: { + shoe1353(amusement887,hot868); + string1354 = 162u8; + print hammer1349; + receipt792 + }; + trade63: receipt792; + downtown65: { + finger1315; + void(); + process1265; + void() + }.whip458.thread257; + change60: wrist790; + grip61: void(); + lamp62: void(); + nerve55: ((10362i16 * 18446744073709543949i16) / 28849i16); +}; +struct quiver1237 { + walk755: i64; + watch752: i32; + truck753; + furniture754: u32; +} +function join1336(yoke1338: u16,society1339: i16) -> void { + building1343 = Answer1090 { + sponge64: -breakfast889; + song57: 16334507693578667643u64; + breath56: 18446744073709551542i8; + stamp58: 13760633430790884151u64; + scarf59: Ground732 { + jeans703: toad917; + wing705: coal1020; + plane707: 28520i16; + books706: 42552u16; + meat704: 3300684183u32; + }.jeans703; + trade63: void(); + downtown65: 49349u16; + change60: 72i8; + grip61: prison1342; + lamp62: help793; + nerve55: (29814i16 + 18446744073709537045i16); + }; + print (amusement887 + amusement887); + sense1340 = 18446744073709551502i8; + territory1341 = void(); + prison1342 = (15627i16 / 18446744073709535357i16); + building1343.lamp62 +}; +month1262 = { + { + anger596; + void(); + receipt792 + }; + void(); + { + eggnog1333 = 3365333673u32; + 23209u16; + lock1173 + }; + party1334 = receipt792; + (hot868 - void()); + { + receipt792; + rat1335 = receipt792 + }; + { + toad917; + void(); + 4420222354791038971u64; + 1714769736184002245i64; + join1336(6481u16,market1337 = hope1122) + }; + void(); + print 29470i16 +}; +man1263 = 718278754i32; +bath1264 = 18446744072834943835i32; +function liquid1275(action1330: u64,aunt1331: i16) -> u64 girl1332 = hot868; +brake1329 = { + receipt792; + goldfish856 +}; +function scarf1274(goldfish1325: u64,stage1326: i64,beetle1327: u16,death1328: i64) -> struct {mass1268: i32; +babies1273: u32; +orange1272: u8; +boot1266: i64; +feast1267: i16; +goldfish1269: u64; +death1270: i64; +discovery1271: _;} brake1329; +function friends1279(use1323: i32,help1324: i32) -> Yoke743 jam982; +expert1282 = jeans1281; +struct banana1286 { + curtain1284; + nerve1283; +} +struct Friction1287 { + page1289; + fight1294: i64; + crayon1291: i32; + color1288: i64; + hate1293: u16; + observation1290: u8; + flight1295: Answer1090; + rabbit1292: u8; +} +function sleet1320(cow1321: u16,magic1322: u64) -> struct {coil1064: i64; +bike1066: u64; +burst1065: u8; +unit1067: i8; +food1068: i16;} void(); +list1319 = sleet1320(finger1315,tendency908); +function bread1301(iron1317: i8) -> North85 experience1318 = Banana1053 { + story1063: list1319; + amusement1055: pet1316; + wrench1062: 627108148i32; + boats1071: 8704856829349394609u64; + action1072: toad917; + reason1073: North85 { + day87: 18446744073364864811i32; + brake86: 7603i16; + apple88: 49948u16; + }; + vessel1069: 5449615295132842444u64; + driving1060: 60u8; + leather1061: pets890; + waves1070: 23i8; + visitor1056: yoke1280; + committee1054: 980398883i32; + locket1059: 15726607166915259240u64; + cast1058: 15483103969342573427u64; + tank1057: 25903i16; +}.reason1073; +popcorn1311 = Ground732 { + jeans703: 89u8; + wing705: { + { + holiday1313 = Note1190 { + unit1067: 18446744073709551527i8; + bike1066: tendency908; + food1068: 18446744073709519088i16; + burst1065: competition1083; + coil1064: amusement887; + }; + Ground237 { + baby241: 10611u16; + fly247: rock1004; + pin244: 7086980907940426273u64; + join239: 2383186000u32; + joke243: 6783785509354885270u64; + lace240: 11i8; + art245: canvas433; + quartz238: process1265; + banana242: nerve1108; + achieve246: breakfast889; + hands248: 11919686311208738421u64; + quilt249: amusement887; + transport250: Uncle122 { + turn126: 18446744073240226309i32; + distribution123: fact1172; + station124: 1084945353u32; + fan129: 17328160075149760032u64; + snow125: 18446744073709551514i8; + orange127: 6785i16; + run128: 18446744073709541995i16; + coal130: help793; + }; + }.achieve246; + force1312 = receipt792; + 11696506230903271867u64; + holiday1313 + }; + linen1314 = print 986539878i32; + void().coal130 + }; + plane707: 2065i16; + books706: finger1315 = pet1316 = prison882.vessel349; + meat704: 1818424669u32; +}; +function park1302(sponge1307: i16,mint1308: u64,sweater1309: i16,toothpaste1310: i64) -> struct {coil1064: i64; +bike1066: u64; +burst1065: u8; +unit1067: i8; +food1068: i16;} Note1190 { + unit1067: 18446744073709551572i8; + bike1066: { + void(); + { + void(); + smash1096; + lock1173 + } + }; + food1068: popcorn1311.jeans703; + burst1065: eyes1242; + coil1064: (amusement887 - (nerve1108 - 13254373908450376357i64)); +}; +meeting1257 = Connection750 { + surprise758: 3750657881052265382i64; + price756: anger596; + skirt759: 17641956098973728350i64; + giraffe763: { + hydrant1259 = { + wire1261 = basket1260; + Answer1090 { + sponge64: 8717630446822798695i64; + song57: 14858509205084428189u64; + breath56: coal1020; + stamp58: 2142178398519138837u64; + scarf59: 530i16; + trade63: void(); + downtown65: 1395u16; + change60: yoke1022; + grip61: 21674i16; + lamp62: receipt792; + nerve55: pets890; + }.lamp62; + basket1260 = wire1261; + 233u8 + }; + wax1258 = canvas433; + void(); + help793; + Cable92 { + patch97: wax1258; + lock98: brother834; + popcorn93: 6043610232985848618i64; + hydrant95: amusement887; + summer94: 129u8; + coast99: Judge45 { + dust50: pets890; + pleasure69: 4177250101u32; + babies48: bomb1097; + icicle46: hope1122; + tray49: 143u8; + flavor70: yoke1022; + stew71: hydrant1259; + order47: brother834; + flame51: quiver1175; + adjustment52: Sand141 { + sister54: Answer1090 { + sponge64: 3713928957362525775i64; + song57: anger596; + breath56: coal1020; + stamp58: 10466689717599141585u64; + scarf59: account1174; + trade63: help793; + downtown65: canvas433; + change60: 18446744073709551535i8; + grip61: 18446744073709538671i16; + lamp62: receipt792; + nerve55: account1174; + }; + cart68: 18446744073709544849i16; + van53: 53721u16; + guitar66: 3694079226u32; + ghost67: 5232109613449281596i64; + }; + }; + cord96: hope1122; + }.summer94; + void(); + void(); + void() + }; + punishment765: (Giraffe251 { + cart255: experience1094; + coach253: 18446744072946345693i32; + company252: pets890; + pigs256: thumb888; + meeting254: month1262; + thread257: 47920u16; + boys258: Ground237 { + baby241: 59664u16; + fly247: goldfish856; + pin244: 8980534526134751184u64; + join239: 2285410141u32; + joke243: tendency908; + lace240: 18446744073709551491i8; + art245: 26303u16; + quartz238: man1263; + banana242: 1537793370497236055i64; + achieve246: 2540788851181578298i64; + hands248: tendency908; + quilt249: thumb888; + transport250: Uncle122 { + turn126: bath1264; + distribution123: 18446744073709551572i8; + station124: 3169643957u32; + fan129: 4648998533872386712u64; + snow125: 18446744073709551544i8; + orange127: 26764i16; + run128: 18446744073709550028i16; + coal130: help793; + }; + }; + }.boys258.art245 + 30820u16); + leg757: process1265 = 18446744072797310164i32; + play762: { + end1277 = Spark1147 { + fruit1153: bomb1097; + muscle1149: 6662777955137060602u64; + church1151: 18446744071928554304i32; + hate1154: canvas433; + apparatus1155: cork1278 = Deer969 { + calendar966: 100i8; + birthday964: 9687u16; + pancake968: 608269133i32; + wind967: 1727979686i32; + direction965: coal1020; + }; + sneeze1148: { + receipt792; + process1265; + void() + }; + feather1152: (18446744073709520946i16 * 18446744073709539033i16); + rake1150: 234u8; + }; + soap1276 = end1277; + scarf1274(liquid1275(tendency908,account1174),soap1276.sneeze1148,canvas433,cellar919); + hot868; + 18446744073709547391i16 + }; + son766: friends1279(740610374i32,yoke1280 = process1265); + baby761: jeans1281 = expert1282.surprise758; + berry751: { + { + { + magic1285 = banana1286 { + curtain1284: nerve1108; + nerve1283: 10610546313222657079u64; + }; + receipt792; + void(); + process1265; + 119u8; + magic1285 + }; + print 4351375246870632855u64; + Cable92 { + patch97: process1265; + lock98: 103i8; + popcorn93: 13104755075219333308i64; + hydrant95: 8549151195558835472i64; + summer94: eyes1242; + coast99: Judge45 { + dust50: 18446744073709543127i16; + pleasure69: nail832; + babies48: smash1096; + icicle46: 18446744073709520143i16; + tray49: 59u8; + flavor70: 5i8; + stew71: process1265; + order47: fact1172; + flame51: 18439i16; + adjustment52: Sand141 { + sister54: Answer1090 { + sponge64: 1322637790368622449i64; + song57: tendency908; + breath56: yoke1022; + stamp58: 17217114425624414183u64; + scarf59: 18446744073709520709i16; + trade63: receipt792; + downtown65: 37153u16; + change60: 18446744073709551608i8; + grip61: 18446744073709539930i16; + lamp62: receipt792; + nerve55: 16837i16; + }; + cart68: 18446744073709534324i16; + van53: 52081u16; + guitar66: nail832; + ghost67: 14287201055000739157i64; + }; + }; + cord96: 785i16; + }.patch97; + { + tendency908; + hope1122; + void() + } + }; + 1630461416181745483u64 + }; + vegetable760: { + territory1303 = silver1304 = Banana1053 { + story1063: Note1190 { + unit1067: trade1299; + bike1066: 4093676543705847603u64; + food1068: 18446744073709539136i16; + burst1065: 219u8; + coil1064: 1228479088715888208i64; + }; + amusement1055: 50602u16; + wrench1062: 1372433470i32; + boats1071: 15186929741025880270u64; + action1072: 15968i16; + reason1073: North85 { + day87: 18446744072174158237i32; + brake86: sneeze830; + apple88: canvas433; + }; + vessel1069: 15853426320473997206u64; + driving1060: 216u8; + leather1061: 18446744073709539384i16; + waves1070: 18446744073709551552i8; + visitor1056: process1265; + committee1054: 18446744071879118001i32; + locket1059: tendency908; + cast1058: 2670194215025122999u64; + tank1057: pets890; + }; + underwear1297 = park1302({ + canvas433; + help793; + void() + },territory1303.cast1058,(hope1122 * 18446744073709532029i16),(nerve1108 / 671130575011415527i64)); + linen1298 = bread1301({ + 1055157580706237742i64; + hot868; + nerve1108; + void(); + void() + }); + copper1296 = Answer1090 { + sponge64: 7151001375520466693i64; + song57: 4795863738024345208u64; + breath56: 18446744073709551568i8; + stamp58: Banana1053 { + story1063: underwear1297; + amusement1055: canvas433; + wrench1062: process1265; + boats1071: hot868; + action1072: experience1094; + reason1073: linen1298; + vessel1069: tendency908; + driving1060: 181u8; + leather1061: 29114i16; + waves1070: 18446744073709551516i8; + visitor1056: process1265; + committee1054: yoke1280; + locket1059: hot868; + cast1058: 6962018319294021229u64; + tank1057: 18446744073709520016i16; + }.locket1059; + scarf59: quiver1175; + trade63: { + 65285u16; + void() + }; + downtown65: 46231u16; + change60: trade1299 = yoke1022; + grip61: 4624i16; + lamp62: grandfather1300 = help793; + nerve55: toad917; + }; + help793; + receipt792; + lock1173; + Friction1287 { + page1289: coal1020; + fight1294: 13575935220821096833i64; + crayon1291: 18446744071720263786i32; + color1288: amusement887; + hate1293: 12953u16; + observation1290: competition1083; + flight1295: copper1296; + rabbit1292: eyes1242; + }.flight1295.lamp62 + }; + vase764: cannon1305 = work1306 = Fowl461 { + curve462: 43792u16; + existence463: 29948i16; + feet464: 18446744073709551568i8; + leg466: canvas433; + clocks467: Father456 { + whip458: argument524; + fifth457: 8258472159454751407u64; + }; + net465: 9011u16; + }.curve462; +}; +class1248 = meeting1257; +function creature1246(playground1251: u16,partner1252: Manager1041,cloud1253: i16,middle1254: i16,pleasure1255: u64,teaching1256: u64) -> Ground237 { + help793; + void() +}.boys258; +function spade1240(horse1245: i16) -> Ground237 creature1246(war1247 = class1248.vase764,Manager1041 { + building1043: Ground732 { + jeans703: pets890; + wing705: 99i8; + plane707: experience1094; + books706: war1247; + meat704: 2979439795u32; + }; + pocket1042: yoke1022; +},18446744073709527214i16,{ + middle1250 = Answer1090 { + sponge64: 4492212055988293054i64; + song57: tendency908; + breath56: 70i8; + stamp58: 1725752140762042724u64; + scarf59: 6898i16; + trade63: void(); + downtown65: 3240u16; + change60: 105i8; + grip61: 16286i16; + lamp62: void(); + nerve55: 18446744073709521725i16; + }; + look1249 = middle1250; + coal1020; + receipt792; + receipt792; + { + void(); + 412u16; + void() + }; + look1249.trade63 +},8311249538501783135u64,10517635612488891630u64); +pull1241 = (18446744072321047002i32 / 18446744073018722459i32); +hobbies1244 = 1636980419i32; +grandmother1243 = Spark1147 { + fruit1153: (148u8 / eyes1242); + muscle1149: (hot868 + 5188727103894247722u64); + church1151: { + thumb888; + 18446744072051605751i32; + void(); + receipt792; + help793 + }; + hate1154: 3239990377u32; + apparatus1155: Deer969 { + calendar966: 94i8; + birthday964: canvas433; + pancake968: 167454173i32; + wind967: hobbies1244; + direction965: wrist790; + }; + sneeze1148: thumb888; + feather1152: quiver1175; + rake1150: 187u8; +}.apparatus1155; +muscle1232 = Spark1147 { + fruit1153: { + frame1235 = (18446744073381453854i32 - 18446744073447795842i32); + cart1238 = 18446744072198757545i32; + pets1236 = quiver1237 { + walk755: 8068972348811884716i64; + watch752: cart1238; + truck753: anger596; + furniture754: 3533911071u32; + }; + brother1233 = son1234 = Connection750 { + surprise758: 13391112105508294951i64; + price756: 14866604897176472221u64; + skirt759: 5850137611293961267i64; + giraffe763: 2496091388u32; + punishment765: canvas433; + leg757: frame1235; + play762: tendency908; + son766: jam982; + baby761: 16093632560536394066i64; + berry751: pets1236; + vegetable760: 199u8; + vase764: canvas433; + }; + brother1233.surprise758; + smash1239 = void() + }; + muscle1149: spade1240(experience1094).pin244; + church1151: { + 1967662607u32; + print experience1094; + quiver1175; + receipt792; + 1003946876i32; + void(); + (); + Answer1090 { + sponge64: cellar919; + song57: 15982428962251425131u64; + breath56: 80i8; + stamp58: 1568800106217878010u64; + scarf59: pets890; + trade63: lock1173; + downtown65: canvas433; + change60: 18446744073709551574i8; + grip61: experience1094; + lamp62: void(); + nerve55: 8477i16; + }.lamp62 + }; + hate1154: 28856u16; + apparatus1155: Deer969 { + calendar966: brother834; + birthday964: (canvas433 + canvas433); + pancake968: (1663140355i32 - 18446744071976516735i32); + wind967: pull1241; + direction965: coal1020; + }; + sneeze1148: 17597058666783275007i64; + feather1152: 21756i16; + rake1150: (eyes1242 = smash1096 / Spark1147 { + fruit1153: eyes1242; + muscle1149: 1917569579959803900u64; + church1151: 1749033320i32; + hate1154: rock1004; + apparatus1155: grandmother1243; + sneeze1148: 326122338752019470i64; + feather1152: experience1094; + rake1150: 65u8; + }.fruit1153); +}; +function wave1220(sister1224: u16,theory1225: u16,bat1226: Cats720,ghost1227: i16,screw1228: u16,waste1229: i16) -> Spark1147 Spark1147 { + fruit1153: { + { + sneeze830; + 9023327325587195924u64; + void(); + void(); + help793 + }; + tendency908; + print 18446744073709551606i8; + lock1173 + }; + muscle1149: coach1230 = theory1225; + church1151: North85 { + day87: 779582504i32; + brake86: 3802i16; + apple88: canvas433; + }.day87; + hate1154: icicle831.quartz238; + apparatus1155: roll1231 = muscle1232.apparatus1155; + sneeze1148: cellar919; + feather1152: ghost1227; + rake1150: bells1095; +}; +governor1179 = wave1220(canvas433,61768u16,beds1221 = Cats720 { + cannon724: 126039006i32; + street721: thumb888; + fold725: Tree348 { + vessel349: canvas433; + stage350: tendency908; + worm351: 31682u16; + chalk352: monkey1222; + robin353: Sand141 { + sister54: Answer1090 { + sponge64: 429481754129552967i64; + song57: anger596; + breath56: brother834; + stamp58: 11810583372933420613u64; + scarf59: account1174; + trade63: lock1173; + downtown65: canvas433; + change60: brother834; + grip61: 18446744073709536262i16; + lamp62: void(); + nerve55: quiver1175; + }; + cart68: pets890; + van53: canvas433; + guitar66: 1558411996u32; + ghost67: thumb888; + }; + }; + chance722: goldfish856; + cave723: goldfish856; +},hands1223.scarf59,(7506u16 / 35511u16),(competition1083 * bells1095)).apparatus1155; +function thumb1194(board1217: u16,porter1218: u64,pollution1219: u8) -> u8 competition1083; +struct Note1190 { + unit1067; + bike1066: u64; + food1068: i16; + burst1065: u8; + coil1064: i64; +} +function hat1180(spot1213: Cats720,stem1214: i64,reaction1215: i16) -> void vegetable1216 = void(); +function idea1204(humor1211: u64) -> void record1212 = void(); +function desk1178(chickens1205: u16,nose1206: u64,town1207: u8,sweater1208: i64) -> struct {sister54: struct {scarf59: i16; +grip61: i16; +change60: _; +song57: u64; +stamp58: _; +nerve55: i16; +lamp62: _; +trade63: void; +sponge64: _; +downtown65: u16; +breath56: _;}; +cart68: i16; +van53: u16; +guitar66: u32; +ghost67: _;} { + parent1209 = void(); + help793; + system1210 = lock1173; + { + 18446744073709548050i16; + 14710641437503639281u64; + lock1173 + }; + 4611997005542076841i64 +}.adjustment52; +bite1177 = desk1178(Spark1147 { + fruit1153: 206u8; + muscle1149: 13782722267413726595u64; + church1151: 18446744073226594115i32; + hate1154: 2290982915u32; + apparatus1155: governor1179; + sneeze1148: 11480223026892067252i64; + feather1152: pets890; + rake1150: 104u8; +}.fruit1153,1629612790u32,{ + school1202 = furniture1201; + letters1199 = Spark1147 { + fruit1153: bomb1097; + muscle1149: icicle1192; + church1151: 18446744073169181386i32; + hate1154: nail832; + apparatus1155: furniture1201 = school1202; + sneeze1148: nail832; + feather1152: 18446744073709523650i16; + rake1150: { + void(); + babies1198; + lock1173; + 541526157i32; + 18u8 + }; + }; + friends1191 = Note1190 { + unit1067: 5i8; + bike1066: 3932553311319813987u64; + food1068: 12212i16; + burst1065: letters1199.fruit1153; + coil1064: connection1200 = 9319916319511197300i64; + }; + price1195 = Note1190 { + unit1067: tank1197 = wrist790; + bike1066: babies1198 = anger596; + food1068: (experience1094 * 18446744073709544859i16); + burst1065: { + nail832; + 16937269127806128325u64 + }; + coil1064: 3809703733546239648i64; + }; + vacation1189 = Banana1053 { + story1063: friends1191; + amusement1055: 65011u16; + wrench1062: 2100664710i32; + boats1071: icicle1192 = 12000126604950344591u64; + action1072: tail1193 = experience1094; + reason1073: North85 { + day87: 764041295i32; + brake86: quiver1175; + apple88: canvas433; + }; + vessel1069: tendency908; + driving1060: thumb1194(canvas433,hot868,competition1083); + leather1061: 17786i16; + waves1070: price1195.unit1067; + visitor1056: (18446744072779445222i32 - 18446744071622135269i32); + committee1054: ring1196 = 18446744071748455783i32; + locket1059: help793; + cast1058: 4785873629306383993u64; + tank1057: 18446744073709536330i16; + }; + ghost1183 = vacation1189.story1063; + copper1184 = smoke1188 = Judge45 { + dust50: account1174; + pleasure69: nail832; + babies48: bomb1097; + icicle46: experience1094; + tray49: competition1083; + flavor70: fact1172; + stew71: 18446744072452298783i32; + order47: wrist790; + flame51: 18323i16; + adjustment52: Sand141 { + sister54: Answer1090 { + sponge64: breakfast889; + song57: tendency908; + breath56: 18446744073709551526i8; + stamp58: anger596; + scarf59: 18446744073709527034i16; + trade63: void(); + downtown65: 7348u16; + change60: 18446744073709551501i8; + grip61: 14027i16; + lamp62: receipt792; + nerve55: 18446744073709524191i16; + }; + cart68: 14422i16; + van53: canvas433; + guitar66: 2069055880u32; + ghost67: nerve1108; + }; + }; + hand1185 = 18446744072593179767i32; + cobweb1182 = Judge45 { + dust50: Banana1053 { + story1063: ghost1183; + amusement1055: canvas433; + wrench1062: 18446744073554908379i32; + boats1071: hot868; + action1072: 14190i16; + reason1073: North85 { + day87: 18446744072783187964i32; + brake86: 18446744073709534306i16; + apple88: canvas433; + }; + vessel1069: 18013176256743478928u64; + driving1060: bomb1097; + leather1061: account1174; + waves1070: 46i8; + visitor1056: 18446744072707184641i32; + committee1054: 18446744073009598276i32; + locket1059: hot868; + cast1058: 6931809212409163616u64; + tank1057: 18446744073709530581i16; + }.leather1061; + pleasure69: 2468112380u32; + babies48: bells1095; + icicle46: copper1184.flame51; + tray49: competition1083; + flavor70: 18446744073709551528i8; + stew71: hand1185; + order47: (fact1172 / 18446744073709551615i8); + flame51: quiver1175; + adjustment52: { + balls1187 = 1641793464i32; + father1186 = balls1187; + void(); + void(); + father1186; + void() + }; + }; + collar1181 = Ground732 { + jeans703: pets890; + wing705: 18446744073709551533i8; + plane707: 18446744073709550446i16; + books706: 6117u16; + meat704: 3009558485u32; + }.books706; + hat1180(Cats720 { + cannon724: collar1181; + street721: nerve1108; + fold725: prison882; + chance722: 3521210733u32; + cave723: 1639516037u32; + },Cable92 { + patch97: 140014172i32; + lock98: 74i8; + popcorn93: 6772571389176280701i64; + hydrant95: breakfast889; + summer94: bomb1097; + coast99: cobweb1182; + cord96: quiver1175; + }.hydrant95,{ + rock1004; + 68u8; + void() + }) +},{ + robin1203 = idea1204(tendency908); + basket914.guitar66; + 939564603u32 +}); +bone1169 = Answer1090 { + sponge64: 8201500799127154481i64; + song57: { + angle1171 = { + 18446744073709522245i16; + void() + }; + butter1170 = Cable92 { + patch97: 86252240i32; + lock98: brother834; + popcorn93: thumb888; + hydrant95: amusement887; + summer94: 129u8; + coast99: Judge45 { + dust50: experience1094; + pleasure69: nail832; + babies48: 16u8; + icicle46: experience1094; + tray49: 68u8; + flavor70: brother834; + stew71: 568209778i32; + order47: yoke1022; + flame51: 20563i16; + adjustment52: angle1171; + }; + cord96: sneeze830; + }; + Driving692 { + boot697: cellar919; + cub695: yoke1022; + stop694: tendency908; + geese693: 28948u16; + pie698: nerve1108; + action699: 18446744072108648749i32; + box696: canvas433; + plants700: butter1170; + }.stop694 + }; + breath56: yoke1022; + stamp58: anger596; + scarf59: fact1172 = brother834; + trade63: lock1173 = void(); + downtown65: 28291u16; + change60: 92i8; + grip61: account1174 = quiver1175 = -22878i16; + lamp62: void(); + nerve55: Judge45 { + dust50: 18446744073709551378i16; + pleasure69: goldfish856; + babies48: 11u8; + icicle46: hope1122; + tray49: bomb1097; + flavor70: coal1020; + stew71: yarn1176; + order47: 18446744073709551537i8; + flame51: 18446744073709546877i16; + adjustment52: bite1177; + }.icicle46; +}; +function bell1082(sisters1165: u16,screw1166: u64,apple1167: u64,lip1168: u64) -> void bone1169.trade63; +struct Answer1090 { + sponge64; + song57: u64; + breath56; + stamp58: u64; + scarf59: i16; + trade63: void; + downtown65; + change60: i8; + grip61: i16; + lamp62: void; + nerve55: i16; +} +function key1133() -> i16 18446744073709525038i16; +function dust1131(ants1163: u32) -> u32 example1164 = 1782488442u32; +struct room1130 { + watch752: i32; + truck753: u64; + furniture754; + walk755: i64; +} +struct Spark1147 { + fruit1153: u8; + muscle1149: u64; + church1151: i32; + hate1154: u32; + apparatus1155: Deer969; + sneeze1148; + feather1152: i16; + rake1150: u8; +} +function pig1144() -> Judge45 { + money1157 = Deer969 { + calendar966: 46i8; + birthday964: 40960u16; + pancake968: art1162 = 18446744073558647363i32; + wind967: { + thumb888; + void(); + 29072u16 + }; + direction965: { + void(); + help793 + }; + }; + moon1159 = { + look1160 = 1546835565i32; + look1160; + tooth1161 = void(); + help793 + }; + brick1156 = Spark1147 { + fruit1153: 82u8; + muscle1149: { + void(); + 3417195893665285838i64; + help793 + }; + church1151: 18446744073367814022i32; + hate1154: 864140458u32; + apparatus1155: money1157; + sneeze1148: key1158 = cellar919; + feather1152: moon1159.brake86; + rake1150: competition1083; + }; + dinner1146 = brick1156.apparatus1155; + school1145 = dinner1146; + school1145.calendar966 +}; +function zipper1134(language1143: struct {position1136: u16; +rhythm1139: i8; +family1137: i16; +cats1138: i16; +curtain1135: _;}) -> Judge45 pig1144(); +struct deer1141 { + rhythm1139; + position1136: u16; + family1137: i16; + cats1138: i16; + curtain1135: i16; +} +income1107 = { + wool1128 = cobweb1127; + minister1140 = deer1141 { + rhythm1139: brother834; + position1136: (12502u16 / (55249u16 * bone1132)); + family1137: police1142 = wrist790; + cats1138: 28647i16; + curtain1135: 3327i16; + }; + attraction1126 = zipper1134(minister1140); + bite1125 = Band315 { + answer322: { + monkey1129 = Answer1090 { + sponge64: 9608794812291583370i64; + song57: anger596; + breath56: 17i8; + stamp58: 3509011441966871784u64; + scarf59: 18446744073709532019i16; + trade63: void(); + downtown65: canvas433; + change60: 18446744073709551517i8; + grip61: pets890; + lamp62: void(); + nerve55: sneeze830; + }; + void(); + Tree348 { + vessel349: 10724u16; + stage350: anger596; + worm351: 10739u16; + chalk352: 18446744073566941972i32; + robin353: Sand141 { + sister54: monkey1129; + cart68: sneeze830; + van53: canvas433; + guitar66: 1462501937u32; + ghost67: 16198078228879973413i64; + }; + }.chalk352 + }; + crow316: 34313u16; + umbrella317: { + Connection750 { + surprise758: 4686929697856203394i64; + price756: tendency908; + skirt759: breakfast889; + giraffe763: goldfish856; + punishment765: 24260u16; + leg757: cobweb1127; + play762: 6653379560869029372u64; + son766: jam982; + baby761: 850931435901742316i64; + berry751: room1130 { + watch752: 18446744073358770840i32; + truck753: hot868; + furniture754: goldfish856; + walk755: 3735134879397141462i64; + }; + vegetable760: 97u8; + vase764: canvas433; + }; + 15709997329581840083i64; + 17904581462147103340u64 + }; + summer321: dust1131(goldfish856); + substance319: bone1132 = 57914u16; + earthquake320: Ground732 { + jeans703: sneeze830; + wing705: 18446744073709551611i8; + plane707: 14173i16; + books706: canvas433; + meat704: rock1004; + }.jeans703; + exchange318: key1133(); + }; + religion1124 = receipt792; + -52i8; + Banana1053 { + story1063: mice1123 = religion1124; + amusement1055: 31026u16; + wrench1062: 1259471816i32; + boats1071: goldfish856; + action1072: { + help793; + receipt792; + void(); + help793 + }; + reason1073: { + void(); + experience1094; + help793; + help793; + void() + }; + vessel1069: bite1125.umbrella317; + driving1060: attraction1126.tray49; + leather1061: 241u8; + waves1070: wrist790; + visitor1056: 18446744073298695435i32; + committee1054: cobweb1127 = wool1128; + locket1059: 745258324716773756u64; + cast1058: 523512318864080341u64; + tank1057: sneeze830; + }.waves1070 +}; +struct Skin1115 { + mother1121: Cats720; + tax1117: Tree348; + oil1118: u8; + company1120: u16; + bubble1116: i16; + badge1119: u8; +} +friends1113 = lock1114 = Skin1115 { + mother1121: Skin1115 { + mother1121: Cats720 { + cannon724: 18446744073226564441i32; + street721: thumb888; + fold725: prison882; + chance722: rock1004; + cave723: 1097071589u32; + }; + tax1117: prison882; + oil1118: bells1095; + company1120: canvas433; + bubble1116: 18446744073709533703i16; + badge1119: 49u8; + }.mother1121; + tax1117: Tree348 { + vessel349: 34029u16; + stage350: 11724713923431423737u64; + worm351: canvas433; + chalk352: 677089283i32; + robin353: basket914; + }; + oil1118: bomb1097; + company1120: Ground732 { + jeans703: sneeze830; + wing705: 18446744073709551570i8; + plane707: 5540i16; + books706: canvas433; + meat704: goldfish856; + }.books706; + bubble1116: hope1122 = 18446744073709536519i16; + badge1119: { + 30343i16; + 60620u16; + receipt792 + }; +}.mother1121; +function show1109(sound1110: i64,balls1111: i32,wine1112: u16) -> Tree348 Tree348 { + vessel349: wine1112; + stage350: 9889044866675310069u64; + worm351: friends1113.fold725.vessel349; + chalk352: balls1111; + robin353: { + sneeze830; + 2923945316965221259i64; + 9061789338087644619u64; + void() + }; +}; +match1106 = Cats720 { + cannon724: income1107; + street721: nerve1108 = amusement887; + fold725: show1109(16611746155606079832i64,1547539669i32,canvas433); + chance722: ({ + void(); + void(); + receipt792 + } - 417088179u32); + cave723: (1062239486u32 * 1814530190u32); +}.cannon724; +goldfish1105 = Tree348 { + vessel349: 60015u16; + stage350: 2720739338117224200u64; + worm351: 42200u16; + chalk352: match1106; + robin353: basket914; +}.robin353; +function shame1100(pencil1104: u16) -> struct {sister54: struct {scarf59: i16; +grip61: i16; +change60: _; +song57: u64; +stamp58: _; +nerve55: i16; +lamp62: _; +trade63: void; +sponge64: _; +downtown65: u16; +breath56: _;}; +cart68: i16; +van53: u16; +guitar66: u32; +ghost67: _;} goldfish1105; +discussion1092 = Judge45 { + dust50: experience1094 = pets890; + pleasure69: 2084829323u32; + babies48: bells1095 = smash1096 = 15u8; + icicle46: 18446744073709521581i16; + tray49: bomb1097 = 32u8; + flavor70: brother834; + stew71: { + { + (10386314656264510148u64 + anger596); + Uncle122 { + turn126: 18446744072929285010i32; + distribution123: 126i8; + station124: nail832; + fan129: 5129373866268280993u64; + snow125: 73i8; + orange127: experience1094; + run128: pets890; + coal130: void(); + }.coal130 + }; + 18446744073709540497i16 + }; + order47: { + void(); + angle1098 = void(); + credit1099 = void() + }; + flame51: void(); + adjustment52: shame1100({ + circle1103 = 18446744073443137466i32; + current1102 = circle1103; + cat1101 = (1721207557i32 - current1102); + Giraffe251 { + cart255: experience1094; + coach253: 1501257402i32; + company252: sneeze830; + pigs256: 17311466428446153347i64; + meeting254: 18446744071629871925i32; + thread257: canvas433; + boys258: Ground237 { + baby241: canvas433; + fly247: rock1004; + pin244: 8533947263381484088u64; + join239: rock1004; + joke243: 376146780964222591u64; + lace240: wrist790; + art245: canvas433; + quartz238: 18446744072108672433i32; + banana242: 5677619074571402004i64; + achieve246: 14691626344078386264i64; + hands248: 14885007928370661478u64; + quilt249: amusement887; + transport250: Uncle122 { + turn126: cat1101; + distribution123: yoke1022; + station124: 1325731005u32; + fan129: hot868; + snow125: 115i8; + orange127: 18446744073709540816i16; + run128: sneeze830; + coal130: void(); + }; + }; + }.coach253; + receipt792 + }); +}; +function meeting1085(wealth1088: i32,game1089: i16) -> struct {sister54: struct {scarf59: i16; +grip61: i16; +change60: _; +song57: u64; +stamp58: _; +nerve55: i16; +lamp62: _; +trade63: void; +sponge64: _; +downtown65: u16; +breath56: _;}; +cart68: i16; +van53: u16; +guitar66: u32; +ghost67: _;} Sand141 { + sister54: Answer1090 { + sponge64: thumb888; + song57: 14427085598424000871u64; + breath56: 124i8; + stamp58: hot868; + scarf59: pets890; + trade63: help793; + downtown65: 27021u16; + change60: 84i8; + grip61: sneeze830; + lamp62: void(); + nerve55: 18446744073709543771i16; + }; + cart68: 17361i16; + van53: 55362u16; + guitar66: ticket1091 = discussion1092.pleasure69; + ghost67: part1093 = canvas433; +}; +quiver1052 = Cable92 { + patch97: Banana1053 { + story1063: boys1074; + amusement1055: 12587u16; + wrench1062: 546632037i32; + boats1071: anger596; + action1072: toad917; + reason1073: cable1075; + vessel1069: 11146255248205032297u64; + driving1060: 86u8; + leather1061: toad917; + waves1070: 73i8; + visitor1056: selection1076; + committee1054: 18446744072080584391i32; + locket1059: 12889030658295871814u64; + cast1058: 4292246665384756481u64; + tank1057: toad917; + }.reason1073.day87; + lock98: (89i8 / power1077.lock98); + popcorn93: { + receipt792; + chair1078 = expansion1079 = receipt792 + }; + hydrant95: { + void(); + (3338351990u32 + 2548147468u32); + void(); + { + void(); + help793 + }.art245; + door1080 = (28465i16 + sneeze830); + void() + }; + summer94: 36u8; + coast99: Judge45 { + dust50: 556954483i32; + pleasure69: { + earthquake1081 = help793; + bell1082(canvas433,7172775345057135624u64,18400879502338961922u64,hot868); + void(); + print 1765261681u32 + }; + babies48: 219u8; + icicle46: -18446744073709528973i16; + tray49: competition1083 = 141u8; + flavor70: wrist790; + stew71: 252u8; + order47: { + print coal1020; + { + thumb888; + help793; + rock1004; + receipt792; + void(); + 17281349232990053053i64 + }; + passenger1084 = 240u8 + }; + flame51: 104u8; + adjustment52: meeting1085((),{ + 13507569071079932223u64; + void(); + void(); + void() + }); + }; + cord96: { + yam1086 = Uncle122 { + turn126: { + void(); + void(); + receipt792 + }; + distribution123: sun1087 = coal1020; + station124: rock1004; + fan129: tendency908; + snow125: 47i8; + orange127: 18446744073709547544i16; + run128: Uncle122 { + turn126: 1784837375i32; + distribution123: 86i8; + station124: goldfish856; + fan129: anger596; + snow125: yoke1022; + orange127: pets890; + run128: 18446744073709539107i16; + coal130: help793; + }.orange127; + coal130: void(); + }; + void(); + 84u8; + yam1086.coal130 + }; +}; +back1051 = quiver1052; +function roll1016(net1048: i64,toad1049: u32,burst1050: u64) -> Judge45 back1051.coast99; +struct rabbit1029 { + trade63; + stamp58: i64; + change60: u16; + lamp62: i8; + sponge64: i32; + nerve55: i16; + downtown65; + breath56: u8; + scarf59: i16; + grip61: i16; + song57: u64; +} +quartz1030 = { + 12290i16; + 18446744073709520095i16 +}; +function daughter1033(prison1046: u64) -> i16 snail1047 = 22474i16; +struct Manager1041 { + building1043; + pocket1042: i16; +} +society1031 = { + trip1038 = 18446744072338332424i32; + vegetable1044 = { + sort1035; + sort1035; + 3600816661u32; + 15193i16 + }; + way1037 = Uncle122 { + turn126: trip1038; + distribution123: yoke1022; + station124: underwear1039 = goldfish856; + fan129: canvas433; + snow125: brass1040 = wrist790; + orange127: vegetable1044.building1043.plane707; + run128: 239u8; + coal130: { + sugar1045 = { + 18446744073203455209i32; + 18446744073709528860i16; + void(); + tendency908; + wound1034; + void(); + void() + }; + (); + print sugar1045; + sort1035; + { + void(); + void(); + nail832; + void(); + 52772u16; + 12937304208873729265i64 + } + }; + }; + { + { + daughter1033(anger596); + void(); + print 13162u16; + canvas433; + void() + }; + print canvas433 + }; + wound1034 = receipt792; + sort1035 = mice1036 = way1037.coal130 +}; +function balloon1018(burn1025: i16,waste1026: u32,baseball1027: u32,cracker1028: u8) -> Sand141 Sand141 { + sister54: rabbit1029 { + trade63: void(); + stamp58: 14802427974074660532i64; + change60: 36068u16; + lamp62: 109i8; + sponge64: quartz1030; + nerve55: burn1025; + downtown65: 39025u16; + breath56: 245u8; + scarf59: pets890; + grip61: pets890; + song57: hot868; + }; + cart68: argument524.cart255; + van53: society1031.punishment765; + guitar66: drain1032 = nail832; + ghost67: 2293309538u32; +}; +bee1019 = 148u8; +function name1021(copper1023: i64,language1024: i32) -> Cable92 { + basket914.van53; + void() +}; +teeth996 = Judge45 { + dust50: { + void(); + void() + }; + pleasure69: rock1004 = goldfish856; + babies48: { + plastic1017 = balloon1018(21320i16,nail832,nail832,254u8); + lunch1013 = plastic1017; + fireman1015 = roll1016(2394253106744164964i64,2306649679u32,13590662090373563710u64).stew71; + transport1012 = fireman1015; + popcorn1011 = { + sneeze830; + help793; + canvas433; + hands1014 = void() + }; + { + water1008 = 2126651663i32; + lunch1009 = { + rock1004; + horses1006; + help793; + 39077u16; + void() + }; + home1007 = Judge45 { + dust50: 21189i16; + pleasure69: goldfish856; + babies48: horses1006; + icicle46: 18446744073709544513i16; + tray49: horses1006; + flavor70: 94i8; + stew71: water1008; + order47: 18446744073709551490i8; + flame51: 10232i16; + adjustment52: lunch1009; + }.babies48; + grip1005 = horses1006 = home1007; + grip1005 + }; + card1010 = Judge45 { + dust50: sneeze830; + pleasure69: nail832; + babies48: popcorn1011; + icicle46: toad917; + tray49: 255u8; + flavor70: 57i8; + stew71: transport1012; + order47: 39i8; + flame51: pets890; + adjustment52: lunch1013; + }.icicle46 + }; + icicle46: sneeze830; + tray49: bee1019.babies48; + flavor70: coal1020 = { + 2850363456u32; + wrist790; + void(); + 7689905151656762153i64; + receipt792; + 10700156464238363577i64; + help793; + 9532637243611565678u64; + void() + }.direction965; + stew71: name1021(622924008113135048i64,18446744071623947522i32).patch97; + order47: yoke1022 = 85i8; + flame51: 4036i16; + adjustment52: { + help793; + { + 4283i16; + 4587820093548946533u64; + (39336u16 + canvas433) + }; + sneeze830; + 1238248198u32; + tendency908 + }; +}; +team1003 = { + void(); + print 15563i16; + void() +}; +function top1001(plate1002: u8) -> Father456 team1003; +function branch995(food997: u16,servant998: i16,mother999: i8,milk1000: u8) -> Giraffe251 top1001(milk1000).whip458; +liquid924 = branch995(canvas433,855i16,18446744073709551503i8,teeth996.babies48).boys258.transport250; +current994 = 214u8; +regret993 = current994; +chess925 = regret993; +sheet962 = -system992 = icicle831.quartz238; +cook985 = 18446744071772879123i32; +function crime984(leaf987: i16,canvas988: i32) -> u8 chairs989 = plate990 = trade991 = 237u8; +kite983 = Deer969 { + calendar966: { + breakfast889; + 17715i16 + }; + birthday964: crime984(15099i16,cook985); + pancake968: { + print (pets890 * pets890); + church986 = 4174273146306007726i64; + 377461617i32; + () + }; + wind967: 1924533372i32; + direction965: 18i8; +}; +stone970 = kite983; +struct Deer969 { + calendar966; + birthday964: u16; + pancake968; + wind967; + direction965: i8; +} +account981 = jam982 = { + 40327u16; + receipt792; + 9230u16 +}; +function dolls977() -> Connection750 Connection750 { + surprise758: 15954883949403054075i64; + price756: play894.boys258.pin244; + skirt759: breakfast889; + giraffe763: goldfish856; + punishment765: bell978 = 54107u16; + leg757: vest979 = 18446744073582358126i32; + play762: 642815341024934044u64; + son766: notebook980 = account981; + baby761: { + receipt792; + void(); + { + bell978; + 18446744073709547791i16; + 6637522472644151265u64 + }; + 9315319316137185857u64; + { + void(); + receipt792 + }; + void() + }; + berry751: { + help793; + 59581u16; + receipt792 + }.berry751; + vegetable760: 23u8; + vase764: 3592u16; +}; +function paint963(laborer974: struct {pancake968: i32; +calendar966: i8; +birthday964: _; +wind967: _; +direction965: _;},goose975: i16,pollution976: u16) -> u16 dolls977().vase764; +receipt973 = { + receipt792; + void(); + receipt792 +}; +function slip961(nest971: i32,sidewalk972: u16) -> Connection750 receipt973; +function spring931(chess959: u8,bun960: i64) -> Connection750 slip961(sheet962,paint963(stone970,18446744073709522553i16,{ + wrist790; + Father456 { + whip458: calendar522; + fifth457: 3958685514685445099u64; + } +})); +copper934 = 1344403838i32; +struct box956 { + scarf59; + stamp58: i8; + breath56: i8; + nerve55: i16; + trade63; + song57: u64; + grip61; + change60: i16; + lamp62; + sponge64: u16; + downtown65: u16; +} +function scarecrow933(brass948: i32) -> i8 yoke949 = { + plot951 = holiday958 = 254u8; + cushion952 = snakes957 = 121u8; + rat955 = box956 { + scarf59: 491i16; + stamp58: 18446744073709551596i8; + breath56: brother834; + nerve55: 18446744073709537359i16; + trade63: receipt792; + song57: 11795107722067749463u64; + grip61: 7397i16; + change60: 18446744073709525407i16; + lamp62: amusement887; + sponge64: 21303u16; + downtown65: canvas433; + }; + size954 = rat955; + shoe953 = Sand141 { + sister54: size954; + cart68: pets890; + van53: 809u16; + guitar66: goldfish856; + ghost67: cellar919; + }; + fiction950 = Cable92 { + patch97: brass948; + lock98: wrist790; + popcorn93: 14005616827088222638i64; + hydrant95: amusement887; + summer94: plot951; + coast99: Judge45 { + dust50: toad917; + pleasure69: goldfish856; + babies48: cushion952; + icicle46: 18446744073709550311i16; + tray49: 109u8; + flavor70: 18446744073709551492i8; + stew71: 18446744071793789950i32; + order47: wrist790; + flame51: sneeze830; + adjustment52: shoe953; + }; + cord96: pets890; + }; + 10031u16; + fiction950.summer94 +}; +function goat932(toy941: u64,slave942: i8,angle943: i16,company944: u32) -> i8 { + grip947 = { + void(); + cellar919 + }; + bushes946 = grip947; + committee945 = (1257660640i32 - -bushes946); + void(); + 12686841520757755839u64; + Uncle122 { + turn126: committee945; + distribution123: brother834; + station124: 2449870967u32; + fan129: toy941; + snow125: 18446744073709551584i8; + orange127: pets890; + run128: 1477i16; + coal130: receipt792; + }.coal130 +}; +function cork930(teeth937: i32,apparel938: i8,ray939: u32,holiday940: u16) -> void help793; +function zipper923(pipe926: u16,loaf927: u64,cows928: i8,tub929: u8) -> void cork930(spring931(tub929,2186088396650194326i64).leg757,goat932(tub929,scarecrow933(copper934),{ + tub929; + 8888750903369305483i64; + void() +},(228087860u32 + 2204563451u32)),4020213590u32,trip935 = clover936 = canvas433); +function pie916(friends920: void,shake921: i64,protest922: u16) -> void zipper923(6767u16,(tendency908 - 17834260014317223234u64),liquid924.distribution123,chess925); +creature912 = basket914 = Sand141 { + sister54: clouds915 { + change60: breakfast889; + stamp58: 13i8; + breath56: (canvas433 * 61374u16); + song57: 2039747362100103589u64; + trade63: pie916(receipt792,6712024198501877413i64,21635u16); + scarf59: toad917 = pets890; + grip61: (4999i16 / toad917); + sponge64: 28u8; + downtown65: 63982u16; + nerve55: (18446744073709522672i16 / 26284i16); + lamp62: void(); + }; + cart68: sneeze830; + van53: 63102u16; + guitar66: { + chicken918 = help793; + void() + }; + ghost67: cellar919 = 6485266034506854536i64; +}; +function top906(believe909: void,hot910: i32,paint911: i16) -> Giraffe251 Giraffe251 { + cart255: { + sneeze830; + thumb888; + 36661u16 + }; + coach253: Tree348 { + vessel349: 9197u16; + stage350: anger596; + worm351: canvas433; + chalk352: 402146563i32; + robin353: creature912; + }.chalk352; + company252: Ground732 { + jeans703: paint911; + wing705: wrist790; + plane707: pets890; + books706: canvas433; + meat704: nail832; + }.jeans703; + pigs256: icicle831.quilt249; + meeting254: science913 = 1697188730i32; + thread257: canvas433; + boys258: Ground237 { + baby241: 44949u16; + fly247: goldfish856; + pin244: 15387313476359225970u64; + join239: 1202571144u32; + joke243: 13716704393064690804u64; + lace240: 18446744073709551591i8; + art245: canvas433; + quartz238: hot910; + banana242: 630668818203670196i64; + achieve246: 5968121175261201030i64; + hands248: 17560126395275595305u64; + quilt249: breakfast889; + transport250: Uncle122 { + turn126: hot910; + distribution123: 65i8; + station124: 1831302842u32; + fan129: 14268769627091797141u64; + snow125: brother834; + orange127: pets890; + run128: 18446744073709521563i16; + coal130: void(); + }; + }; +}; +drink902 = Fowl461 { + curve462: 16779u16; + existence463: { + cemetery904 = 18446744073707258956i32; + quilt903 = cemetery904; + 456053160i32; + void(); + 49565u16 + }; + feet464: 18446744073709551504i8; + leg466: tree905(47888u16,116i8).crow316; + clocks467: Father456 { + whip458: top906(help793,beggar907,18446744073709544792i16); + fifth457: tendency908 = hot868; + }; + net465: 60077u16; +}; +building897 = ({ + void(); + void(); + brother834 +} * drink902.feet464); +rate898 = { + void(); + { + 18446744073709522106i16; + { + 12710667498879825321i64; + canvas433 + }; + { + breakfast889; + 37699u16; + receipt792; + void() + } + }; + 2651412005u32 +}; +dogs899 = 18446744073215069334i32; +mark900 = 1869600923i32; +function oven895(support896: u32) -> Father456 Father456 { + whip458: Giraffe251 { + cart255: 18446744073709532916i16; + coach253: building897; + company252: pets890; + pigs256: amusement887; + meeting254: rate898; + thread257: canvas433; + boys258: Ground237 { + baby241: canvas433; + fly247: 2751994233u32; + pin244: anger596; + join239: 1749773461u32; + joke243: 3459160801976237592u64; + lace240: brother834; + art245: canvas433; + quartz238: dogs899; + banana242: 9630593702004399869i64; + achieve246: 16811000704379738530i64; + hands248: hot868; + quilt249: 642273414093413155i64; + transport250: Uncle122 { + turn126: mark900; + distribution123: 31i8; + station124: 1467173889u32; + fan129: 11050520899829445842u64; + snow125: wrist790; + orange127: 25519i16; + run128: sneeze830; + coal130: receipt792; + }; + }; + }; + fifth457: time901 = (hot868 - anger596); +}; +surprise893 = oven895({ + print 60u8; + void(); + print 1562428619i32; + pets890 +}.fly247); +copy862 = Giraffe251 { + cart255: pets890 = (sneeze830 + sneeze830); + coach253: Uncle122 { + turn126: country891; + distribution123: wrist790; + station124: nail832; + fan129: hot868; + snow125: 104i8; + orange127: 25083i16; + run128: 12937i16; + coal130: help793; + }.run128; + company252: { + receipt792; + help793; + 195u8 + }.flame51; + pigs256: 5342147479509460852i64; + meeting254: cabbage892; + thread257: (62706u16 + Fowl461 { + curve462: canvas433; + existence463: 18446744073709524780i16; + feet464: 66i8; + leg466: canvas433; + clocks467: surprise893; + net465: 19391u16; + }.curve462); + boys258: play894 = edge521.boys258; +}.meeting254; +boats863 = breakfast889 = 10550532610312977411i64; +feeling867 = 112u8; +desire884 = amusement887 = thumb888 = 4708064574238726687i64; +function hospital883(suggestion885: i64,knowledge886: u64) -> Tree348 prison882; +girls869 = hospital883((18302624177772031977i64 / desire884),10713693747123700774u64); +trains881 = prison882 = (); +function nose876(clouds877: i16,decision878: i8) -> Tree348 protest879 = level880 = trains881; +function crayon874(turkey875: u16) -> Tree348 nose876(18446744073709548514i16,().lace240); +function class866(sand870: i16,fruit871: u64,company872: i32,time873: i8) -> Sand141 crayon874(49482u16).robin353; +toes864 = class866(feeling867,hot868 = anger596,girls869.chalk352,wrist790).ghost67; +company865 = { + (); + canvas433 +}.patch97; +throne838 = Uncle122 { + turn126: { + store842 = (); + love843 = ant855(); + cry845 = ({ + shock854 = 144u8; + children848 = shock854; + patch847 = dad853 { + furniture754: nail832; + walk755: 4206081744083666875i64; + watch752: 18446744071574927457i32; + truck753: anger596; + }; + sister849 = 1213938652i32; + station852 = 471193948i32; + flower850 = station852; + insurance851 = 12311990697524066954i64; + fish846 = Fowl461 { + curve462: 10383u16; + existence463: sneeze830; + feet464: brother834; + leg466: canvas433; + clocks467: Father456 { + whip458: Giraffe251 { + cart255: sneeze830; + coach253: sister849; + company252: 3618i16; + pigs256: 4974029806263443433i64; + meeting254: flower850; + thread257: canvas433; + boys258: Ground237 { + baby241: canvas433; + fly247: nail832; + pin244: 9339364935546213751u64; + join239: nail832; + joke243: 17891834011752461247u64; + lace240: 20i8; + art245: 2123u16; + quartz238: 1328393572i32; + banana242: insurance851; + achieve246: 16834063545160266051i64; + hands248: anger596; + quilt249: 1978867145746494153i64; + transport250: Uncle122 { + turn126: 881378742i32; + distribution123: 18446744073709551561i8; + station124: 629881080u32; + fan129: 4667929129889277676u64; + snow125: 18446744073709551551i8; + orange127: 14776i16; + run128: sneeze830; + coal130: help793; + }; + }; + }; + fifth457: 10184422698226395994u64; + }; + net465: canvas433; + }; + void(); + void(); + Connection750 { + surprise758: 18068625170625992670i64; + price756: anger596; + skirt759: 16626703115121317821i64; + giraffe763: nail832; + punishment765: canvas433; + leg757: 18446744071979715519i32; + play762: 12912312391352155532u64; + son766: Yoke743 { + school746: fish846; + pail744: nail832; + show745: anger596; + }; + baby761: 6890417578416619189i64; + berry751: patch847; + vegetable760: children848; + vase764: 1593u16; + } + } + 4850752480959794299i64); + crowd844 = cry845; + veil840 = cemetery841 = Judge45 { + dust50: 29713i16; + pleasure69: nail832; + babies48: 53u8; + icicle46: sneeze830; + tray49: store842; + flavor70: 68i8; + stew71: 1767888590i32; + order47: 18446744073709551495i8; + flame51: sneeze830; + adjustment52: Sand141 { + sister54: love843; + cart68: sneeze830; + van53: canvas433; + guitar66: 1827289376u32; + ghost67: crowd844; + }; + }; + nut839 = veil840.babies48; + sneeze830; + 18446744073709535291i16; + nut839 + }; + distribution123: (); + station124: goldfish856 = Ground237 { + baby241: canvas433; + fly247: nail832; + pin244: 418045298488225636u64; + join239: 2282063060u32; + joke243: 3668792185704906401u64; + lace240: wrist790; + art245: canvas433; + quartz238: rat857; + banana242: 12692270432246307279i64; + achieve246: 14293702952853459651i64; + hands248: anger596; + quilt249: elbow858; + transport250: reaction859; + }.fly247; + fan129: anger596; + snow125: power860(brother834).lace240; + orange127: (18446744073709547120i16 / Giraffe251 { + cart255: sneeze830; + coach253: 18446744071690192473i32; + company252: 14762i16; + pigs256: 1238058878545071026i64; + meeting254: can861; + thread257: canvas433; + boys258: Ground237 { + baby241: canvas433; + fly247: goldfish856; + pin244: 14008409178366823814u64; + join239: 2962796317u32; + joke243: 2089725834994366644u64; + lace240: brother834; + art245: canvas433; + quartz238: copy862; + banana242: boats863; + achieve246: 14355106287485785919i64; + hands248: 7938530183724093811u64; + quilt249: toes864; + transport250: Uncle122 { + turn126: company865; + distribution123: wrist790; + station124: 2001002513u32; + fan129: 2360539911321910556u64; + snow125: 97i8; + orange127: sneeze830; + run128: 29305i16; + coal130: void(); + }; + }; + }.company252); + run128: 18446744073709535141i16; + coal130: void(); +}; +island824 = icicle831 = Ground237 { + baby241: 34447u16; + fly247: 149197436u32; + pin244: 10348527566536313207u64; + join239: nail832 = shame833; + joke243: { + void(); + receipt792; + 16732813052407451964u64 + }; + lace240: brother834 = 22i8; + art245: 7281u16; + quartz238: plough835; + banana242: { + receipt792; + void(); + 2494356856u32; + receipt792; + receipt792; + sneeze830; + 18446744073709551505i8; + receipt792 + }; + achieve246: 9421272071498075366i64; + hands248: 10574208227647697013u64; + quilt249: 1750781366172878736i64; + transport250: Ground237 { + baby241: 53288u16; + fly247: 3086121508u32; + pin244: anger596; + join239: 3975498551u32; + joke243: 1290869582844185737u64; + lace240: wrist790; + art245: canvas433; + quartz238: 365198833i32; + banana242: berry836; + achieve246: sky837; + hands248: anger596; + quilt249: 17584157608989901035i64; + transport250: throne838; + }.transport250; +}; +theory827 = 23009i16; +umbrella829 = sneeze830 = 30542i16; +function idea823(year826: i64) -> i16 ((18446744073709540876i16 - (theory827 / 18446744073709532736i16)) + -shame828 = umbrella829); +function cherry820(cats822: i64) -> Giraffe251 Giraffe251 { + cart255: idea823(island824.quilt249); + coach253: rifle825 = canvas433; + company252: 18446744073709529445i16; + pigs256: 16627360489785713124i64; + meeting254: { + help793; + anger596; + help793 + }.action699; + thread257: 40483u16; + boys258: calendar522.boys258; +}; +hammer819 = cherry820(page821).boys258; +function zipper816(flower817: i16,breath818: i8) -> Ground237 hammer819; +function class780(attraction815: i32) -> Ground237 zipper816(5982i16,75i8); +struct guide637 { + stamp58: u16; + song57: u64; + nerve55; + lamp62; + downtown65: u16; + breath56: u16; + grip61: i16; + change60; + trade63: void; + sponge64: i8; + scarf59: i16; +} +function ghost800(need811: u16,beds812: u64) -> u16 sun813 = farm814 = need811; +request789 = { + request810 = help793; + scent809 = request810; + ducks808 = scent809; + motion807 = ducks808; + lizards795 = { + goose806 = 18446744073709551615i8; + swim805 = goose806; + jellyfish804 = swim805; + scissors803 = jellyfish804; + snails802 = 54138u16; + river801 = { + 18446744073709551581i8; + void() + }; + property796 = { + hill799 = 14u8; + able798 = hill799; + porter797 = able798; + anger596; + 885167722104434333u64; + void(); + 58u8; + porter797 + }; + Uncle122 { + turn126: property796; + distribution123: 14i8; + station124: 1383435715u32; + fan129: 16954687732820973702u64; + snow125: 18446744073709551554i8; + orange127: 441i16; + run128: 18446744073709538669i16; + coal130: void(); + }.coal130; + 8601i16; + ghost800(23364u16,11421655462706407140u64); + Uncle122 { + turn126: river801; + distribution123: 40i8; + station124: snails802; + fan129: 13256782901830623605u64; + snow125: 11i8; + orange127: scissors803; + run128: 19399i16; + coal130: help793; + }.coal130 + }; + rest794 = { + 3844u16; + void() + }; + lizards795.coal130; + motion807.coal130 +}.robin353; +bomb791 = 113u8; +crush785 = Uncle122 { + turn126: 18446744073000061220i32; + distribution123: { + snow787 = 249u8; + snow787; + print 19377u16 + }; + station124: ({ + steel788 = void(); + steel788 + }.giraffe763 - request789.guitar66); + fan129: 6717849892362243204u64; + snow125: wrist790 = 7i8; + orange127: ().cord96; + run128: bomb791; + coal130: receipt792 = help793 = void(); +}; +land786 = calendar522.boys258; +function flesh782(statement783: u16,sheep784: i16) -> void Uncle122 { + turn126: crush785.turn126; + distribution123: 125i8; + station124: land786.join239; + fan129: (1177733682742221555u64 + anger596); + snow125: (25i8 - 18446744073709551502i8); + orange127: 18446744073709542169i16; + run128: (); + coal130: void(); +}.coal130; +stomach608 = { + nest643 = (sack642 + 18446744073709538188i16); + sky641 = spot680; + umbrella640 = rose647; + sweater639 = flesh782(45618u16,32i8); + suit638 = sack642; + step636 = (); + stream635 = frame781; + banana652 = class780(frame781 = 18446744073041556369i32); + mouth653 = { + birds776 = Tree348 { + vessel349: motion777 = canvas433; + stage350: 1331723934u32; + worm351: lumber778 = canvas433; + chalk352: 1804322242i32; + robin353: sweater779(rose647,22801u16,12222i16); + }; + frogs774 = fog775 = birds776; + stamp772 = whistle773 = Cats720 { + cannon724: 18446744072205648454i32; + street721: spot680; + fold725: frogs774; + chance722: 368088972u32; + cave723: size683; + }; + badge771 = stamp772.cannon724; + scent767 = bed768 = lizards769 = park770 { + truck753: 5233364381745081661u64; + furniture754: size683; + watch752: badge771; + walk755: stitch682; + }; + nest748 = bomb749 = Connection750 { + surprise758: 9393208481434041918i64; + price756: 14781862973168012584u64; + skirt759: 7125681358732166956i64; + giraffe763: size683; + punishment765: 57326u16; + leg757: 18446744071839476702i32; + play762: anger596; + son766: spark747; + baby761: stitch682; + berry751: scent767; + vegetable760: pest650; + vase764: 42232u16; + }.son766; + dime742 = spark747 = nest748.school746; + shape741 = dime742; + 18446744073709551527i8; + void(); + ring740 = shape741.existence463 + }; + wall658 = alley736({ + van739 = void(); + achieve737 = lamp738 = van739; + (7626u16 * canvas433); + 1602548054i32; + achieve737 + }); + ice662 = note733 = { + cook734 = void(); + cook734; + stitch682; + 94u8; + 14499814513699351622i64; + berry735 = sack642; + { + 17646444294484075500i64; + canvas433; + 1124280260i32 + } + }; + knee687 = 18446744071649635344i32; + winter688 = { + cake708 = Ground732 { + jeans703: sack642; + wing705: 18446744073709551524i8; + plane707: sack642; + books706: canvas433; + meat704: 2327040709u32; + }; + learning731 = dirt730; + advice709 = dirt730 = learning731; + potato729 = person727; + bucket726 = person727 = hook728 = potato729; + bedroom719 = bucket726.fold725.robin353; + growth712 = bedroom719; + riddle718 = instrument686.coast99; + school713 = riddle718; + mitten715 = mitten716(squirrel717 = size683,(pest650 * rose647)); + attack710 = thing711 { + change60: 7557i16; + grip61: growth712.cart68; + nerve55: sack642; + sponge64: (27577i16 + 18446744073709523499i16); + downtown65: 63419u16; + song57: (); + breath56: school713; + trade63: void(); + stamp58: { + void(); + 54361u16 + }; + lamp62: wave714 = 9156611279518668128i64; + scarf59: mitten715.cart68; + }; + shock701 = Driving692 { + boot697: 9593768434638797105i64; + cub695: shelf702(cake708); + stop694: (2266215042566971581u64 * anger596); + geese693: canvas433; + pie698: stitch682; + action699: 18446744072254316554i32; + box696: (canvas433 - 45680u16); + plants700: Cable92 { + patch97: 1011433879i32; + lock98: ocean665; + popcorn93: spot680; + hydrant95: 17845496570834322114i64; + summer94: 69u8; + coast99: Judge45 { + dust50: sack642; + pleasure69: 602406263u32; + babies48: view649; + icicle46: 18446744073709548471i16; + tray49: 126u8; + flavor70: rain645; + stew71: advice709; + order47: 18446744073709551513i8; + flame51: 18446744073709539403i16; + adjustment52: Sand141 { + sister54: attack710; + cart68: sack642; + van53: 15165u16; + guitar66: 3236029688u32; + ghost67: spot680; + }; + }; + cord96: 18446744073709533357i16; + }; + }; + { + achieve691 = 14099643i32; + size690 = Uncle122 { + turn126: achieve691; + distribution123: rain645; + station124: size683; + fan129: 13021958697107407782u64; + snow125: 3i8; + orange127: sack642; + run128: sack642; + coal130: void(); + }.coal130; + rod689(meat681,sack642); + void(); + size690 + }; + shock701.plants700 + }; + tail664 = instrument686 = Cable92 { + patch97: knee687; + lock98: ocean665; + popcorn93: stitch682; + hydrant95: 7029582559233693157i64; + summer94: 47u8; + coast99: Judge45 { + dust50: sack642; + pleasure69: 2612337421u32; + babies48: 217u8; + icicle46: sack642; + tray49: pest650; + flavor70: 18446744073709551539i8; + stew71: 1408070482i32; + order47: 18446744073709551514i8; + flame51: sack642; + adjustment52: Sand141 { + sister54: winter688; + cart68: 7723i16; + van53: canvas433; + guitar66: size683; + ghost67: stitch682; + }; + }; + cord96: 18446744073709530388i16; + }.hydrant95; + sticks678 = size683 = { + hair685 = void(); + governor684 = hair685; + void(); + sack642; + void(); + governor684 + }.station124; + flight679 = spot680 = meat681 = stitch682 = 4565586087257219206i64; + sock655 = Sand141 { + sister54: flock656 { + stamp58: 23i8; + trade63: { + trail657 = void(); + 3886398883u32; + trail657 + }; + sponge64: 18446744073709551588i8; + downtown65: wall658.worm351; + scarf59: (sack642 / 1768i16); + song57: 10198754612984230929u64; + breath56: { + hobbies659 = void(); + canvas433; + void(); + canvas433; + void(); + hobbies659 + }; + change60: { + disgust661 = 1369637419i32; + drop660 = disgust661; + drop660; + void() + }; + grip61: ice662.orange127; + lamp62: reason663(tail664,anger596); + nerve55: sack642; + }; + cart68: ocean665 = 18i8; + van53: { + song667 = { + money677 = void(); + stem669; + money677 + }; + seed670 = { + person676 = void(); + father675 = person676; + manager674 = father675; + chance673 = manager674; + pen672 = void(); + base671 = pen672; + base671; + void(); + chance673 + }; + glue668 = stem669 = seed670; + parent666 = Sand141 { + sister54: song667; + cart68: sack642; + van53: canvas433; + guitar66: glue668; + ghost67: 11655463405831633281i64; + }; + parent666.ghost67 + }; + guitar66: sticks678; + ghost67: flight679; + }; + structure634 = step651(anger596,canvas433,banana652.achieve246,(mouth653.umbrella317 + 10u8),notebook654 = 1780585399u32,186u8,sock655.guitar66,18446744073709551595i8); + wall648 = view649 = pest650 = 32u8; + twist633 = rose647 = wall648; + religion632 = print { + void(); + 4416u16; + print sack642 + }; + office631 = sack642; + surprise646 = rain645; + laugh630 = rain645 = surprise646; + theory629 = (); + feeling644 = 693430279i32; + clock628 = feeling644; + Uncle122 { + turn126: 18446744073639827685i32; + distribution123: Uncle122 { + turn126: clock628; + distribution123: 29i8; + station124: 1968641066u32; + fan129: anger596; + snow125: theory629; + orange127: laugh630; + run128: office631; + coal130: religion632; + }.distribution123; + station124: Judge45 { + dust50: 18752i16; + pleasure69: 1223469101u32; + babies48: twist633; + icicle46: 25564i16; + tray49: structure634; + flavor70: 21i8; + stew71: stream635; + order47: 18446744073709551571i8; + flame51: step636; + adjustment52: Sand141 { + sister54: guide637 { + stamp58: canvas433; + song57: 3949303690742805897u64; + nerve55: 18446744073709527735i16; + lamp62: 2417906907517631519i64; + downtown65: 30978u16; + breath56: canvas433; + grip61: suit638; + change60: 3693075588u32; + trade63: sweater639; + sponge64: 18446744073709551560i8; + scarf59: 14915i16; + }; + cart68: 13409i16; + van53: canvas433; + guitar66: umbrella640; + ghost67: sky641; + }; + }.pleasure69; + fan129: (); + snow125: 33i8; + orange127: sack642 = nest643; + run128: 75u8; + coal130: void(); + }.coal130 +}; +shop591 = Uncle122 { + turn126: { + force592 = void(); + force592; + print 111i8; + edge521.boys258.achieve246 + }; + distribution123: Ground237 { + baby241: Fowl461 { + curve462: canvas433; + existence463: power593; + feet464: 32i8; + leg466: canvas433; + clocks467: Father456 { + whip458: argument524; + fifth457: loaf594; + }; + net465: canvas433; + }.net465; + fly247: force595(18446744073709525752i16); + pin244: anger596 = 3359391571392341306u64; + join239: 2825818364u32; + joke243: { + push597 = void(); + push597 + }; + lace240: yoke598; + art245: { + void(); + void(); + 10481i16; + canvas433; + void() + }; + quartz238: Uncle122 { + turn126: hobbies599; + distribution123: quilt600; + station124: square601; + fan129: 11817550582795100578u64; + snow125: 18446744073709551521i8; + orange127: page602; + run128: wing603; + coal130: void(); + }.turn126; + banana242: 7734038683645683393i64; + achieve246: 14067622175667481121i64; + hands248: anger596; + quilt249: (river604 - 7200786890774988981i64); + transport250: (); + }.lace240; + station124: 3651472414u32; + fan129: driving605(rabbits606,58i8).start30; + snow125: record607; + orange127: 18446744073709544033i16; + run128: stomach608; + coal130: print { + market613 = book621.coal130; + kiss612 = 23581i16; + jeans611 = 24u8; + street610 = 18446744073709551564i8; + toothbrush614 = wilderness627 = 1880927244i32; + bear615 = 6864493539045323042i64; + drop617 = (2329900032u32 + 2108363957u32); + quarter618 = (109365919192572939i64 - 1769698093364865569i64); + rings622 = book621.turn126; + pot623 = 18446744073709551489i8; + comb624 = { + believe626 = void(); + believe626; + 18446744072484389956i32 + }; + kettle625 = 10809i16; + person620 = book621 = Uncle122 { + turn126: rings622; + distribution123: pot623; + station124: 4030609975u32; + fan129: anger596; + snow125: comb624; + orange127: 18446744073709547327i16; + run128: kettle625; + coal130: void(); + }; + society619 = person620; + earthquake616 = Ground237 { + baby241: canvas433; + fly247: drop617; + pin244: 9430452495556557620u64; + join239: 993201966u32; + joke243: 13258519973859903780u64; + lace240: 19i8; + art245: canvas433; + quartz238: 18446744073215398120i32; + banana242: quarter618; + achieve246: 14140881459770837101i64; + hands248: anger596; + quilt249: 3463344569961767608i64; + transport250: society619; + }; + salt609 = Giraffe251 { + cart255: 18446744073709532428i16; + coach253: toothbrush614; + company252: 18446744073709536130i16; + pigs256: bear615; + meeting254: 18446744072242747502i32; + thread257: 2556u16; + boys258: earthquake616; + }.meeting254; + Uncle122 { + turn126: salt609; + distribution123: street610; + station124: 938809811u32; + fan129: 1480031219447652679u64; + snow125: 125i8; + orange127: jeans611; + run128: kiss612; + coal130: market613; + }.coal130 + }; +}; +function plant566(hospital570: u8,watch571: i16) -> i32 Giraffe251 { + cart255: { + snakes573 = 18368160491978427387u64; + spring572 = snakes573; + void(); + spring572 + }; + coach253: 18446744073027572889i32; + company252: Judge45 { + dust50: 18446744073709521709i16; + pleasure69: furniture574; + babies48: hospital570; + icicle46: watch571; + tray49: 111u8; + flavor70: 68i8; + stew71: boy575; + order47: string576; + flame51: 18446744073709549192i16; + adjustment52: Sand141 { + sister54: income577 { + stamp58: canvas433; + downtown65: canvas433; + lamp62: 17082716668496897154i64; + trade63: heart578; + sponge64: home579; + breath56: 18446744073709519044i16; + scarf59: 26057i16; + change60: watch571; + grip61: 18446744073709544424i16; + song57: 7041434077059359460u64; + nerve55: watch571; + }; + cart68: 9242i16; + van53: 64358u16; + guitar66: 4028942620u32; + ghost67: 14652582293799812554i64; + }; + }.dust50; + pigs256: 7230944893669115653i64; + meeting254: 18446744071943025000i32; + thread257: class580.leg466; + boys258: Giraffe251 { + cart255: 18446744073709522386i16; + coach253: sheet581; + company252: 18446744073709531686i16; + pigs256: 8849986685437215189i64; + meeting254: jeans582; + thread257: 44406u16; + boys258: Ground237 { + baby241: 6750u16; + fly247: parent583; + pin244: 7652967445642923385u64; + join239: page584; + joke243: string585; + lace240: profit586; + art245: 10331u16; + quartz238: rat587; + banana242: needle588; + achieve246: list589; + hands248: pickle590; + quilt249: 18176290441174178359i64; + transport250: shop591; + }; + }.boys258; +}.coach253; +function swim556(page565: i64) -> i32 plant566({ + { + cow568 = void(); + noise567 = cow568; + void(); + 18446744073362433728i32; + noise567 + }; + -21910i16; + void() +},gun569); +function land564() -> Uncle122 { + 88u8; + void() +}; +function hospital502(knot525: u16,fang526: u16,tin527: i64) -> i16 { + cloud563 = land564(); + meeting560 = -cloud563.distribution123; + rat559 = ((18446744073709551559i8 + meeting560) - { + zinc562 = void(); + distance561 = zinc562; + void(); + distance561; + girls540 + }); + pipe558 = rat559; + lunchroom557 = pipe558; + idea530 = lunchroom557; + seat544 = 119i8; + leg545 = (swim556(tin527) * 102i8); + drain546 = { + fish555 = void(); + eyes554 = fish555; + cloud553 = eyes554; + level552 = void(); + 10376769880591420749i64; + level552; + void(); + cloud553 + }.order47; + day547 = horn550 = size551(4103u16,tin527); + lettuce548 = spiders549(34931u16,4711285691010633772u64); + smell531 = (Judge45 { + dust50: 517i16; + pleasure69: 3367791637u32; + babies48: 161u8; + icicle46: girls540; + tray49: 120u8; + flavor70: seat544; + stew71: leg545; + order47: drain546; + flame51: girls540; + adjustment52: day547; + }.dust50 + lettuce548.icicle46); + fang532 = 177u8; + shape533 = 3291048517u32; + night534 = { + cherry541 = { + cent543 = void(); + arm542 = void(); + 686383458171059296i64; + arm542; + cent543 + }; + print girls540; + cherry541 + }; + humor537 = 29261u16; + crook535 = Uncle122 { + turn126: humor537; + distribution123: { + turkey539 = 18446744072548995034i32; + relation538 = void(); + relation538; + turkey539 + }; + station124: 1934843324u32; + fan129: (loaf536 / loaf536); + snow125: 18446744073709551583i8; + orange127: 18446744073709521103i16; + run128: girls540 = 18446744073709526830i16; + coal130: void(); + }; + action529 = Father456 { + whip458: Giraffe251 { + cart255: 18446744073709531692i16; + coach253: idea530; + company252: smell531; + pigs256: 694358935600428640i64; + meeting254: fang532; + thread257: 38326u16; + boys258: Ground237 { + baby241: 11626u16; + fly247: shape533; + pin244: 3625486750094365665u64; + join239: night534; + joke243: 2216546854283088289u64; + lace240: 72i8; + art245: fang526; + quartz238: 465035495i32; + banana242: tin527; + achieve246: 3021612365447362818i64; + hands248: 15634575354948236445u64; + quilt249: tin527; + transport250: crook535; + }; + }; + fifth457: loaf536 = 9675232877362640394u64; + }; + Fowl461 { + curve462: knot525; + existence463: 18883i16; + feet464: 18446744073709551523i8; + leg466: 4168u16; + clocks467: appliance528 = action529; + net465: (knot525 / 20363u16); + } +}; +struct Fowl461 { + curve462: u16; + existence463: i16; + feet464: i8; + leg466: u16; + clocks467; + net465: u16; +} +struct Father456 { + whip458: Giraffe251; + fifth457: u64; +} +carpenter523 = argument524 = calendar522; +trees520 = edge521 = calendar522 = carpenter523.boys258.banana242; +acoustics519 = trees520; +function corn511(payment516: u8,chalk517: u64,engine518: i32) -> i64 acoustics519; +function chair441(wave510: i64) -> Judge45 { + property513 = { + void(); + canvas433; + void(); + wave510 + }; + price514 = { + dust515 = 18446744072172266895i32; + 18446744073709551522i8; + 3270483429u32; + void(); + dust515 + }; + record512 = Father456 { + whip458: price514; + fifth457: 2722794621278636200u64; + }.fifth457; + corn511(182u8,record512,property513) +}.coast99; +crow426 = chair441((Ground237 { + baby241: canvas433; + fly247: afternoon442; + pin244: reading443; + join239: blow444; + joke243: 6325290637608875956u64; + lace240: 2i8; + art245: canvas433; + quartz238: 18446744072067888135i32; + banana242: 13612170237704477531i64; + achieve246: 4970261625026843286i64; + hands248: 6324380012339670954u64; + quilt249: body445; + transport250: sock446; +}.banana242 / { + creature509 = rule508; + chalk507 = rule508 = creature509; + riddle506 = chalk507; + observation452 = riddle506.coal130; + collar451 = 12i8; + yarn468 = 32i8; + fork505 = 14070698895357297889i64; + apparel503 = help504 = fork505; + produce469 = hospital502(canvas433,2066u16,apparel503); + texture500 = lip501(9599021326794501907i64); + creator498 = texture500.answer322; + button499 = join453; + apple497 = Band315 { + answer322: creator498; + crow316: 47318u16; + umbrella317: pest496; + summer321: button499; + substance319: loss490; + earthquake320: 18446744073709533917i16; + exchange318: 18446744073709523998i16; + }.umbrella317; + chain476 = pest496 = apple497; + drum495 = (); + rod494 = drum495; + increase477 = rod494; + muscle478 = 13611906498104236020u64; + back479 = 65i8; + increase480 = 18446744073709540380i16; + power493 = 18446744073709551606i8; + approval492 = power493; + dinner491 = approval492; + art482 = dinner491; + jeans489 = loss490 = powder488; + bit483 = powder488 = jeans489; + camera484 = 5i8; + hope485 = 38u8; + recess486 = middle487 = void(); + sense481 = Uncle122 { + turn126: join453; + distribution123: art482; + station124: bit483; + fan129: 15776585966909429421u64; + snow125: camera484; + orange127: hope485; + run128: 18446744073709547585i16; + coal130: recess486; + }; + curve470 = root475 = Ground237 { + baby241: canvas433; + fly247: 443337932u32; + pin244: chain476; + join239: increase477; + joke243: muscle478; + lace240: back479; + art245: 51707u16; + quartz238: 1202595451i32; + banana242: 3710190344673940712i64; + achieve246: increase480; + hands248: 2658187969442277037u64; + quilt249: 4449019603826973850i64; + transport250: sense481; + }; + whip474 = 10532763949209007717u64; + hammer473 = whip474; + chain472 = hammer473; + horses471 = chain472; + pin460 = Fowl461 { + curve462: canvas433; + existence463: 10112i16; + feet464: yarn468; + leg466: canvas433; + clocks467: Father456 { + whip458: Giraffe251 { + cart255: produce469; + coach253: 18446744073064383402i32; + company252: 10456i16; + pigs256: 10573915539132333607i64; + meeting254: 850097622i32; + thread257: 63876u16; + boys258: curve470; + }; + fifth457: horses471; + }; + net465: canvas433; + }.clocks467; + sticks459 = pin460; + comparison455 = sticks459.whip458; + show454 = comparison455; + good-bye450 = show454.cart255; + substance449 = 4209813238u32; + achiever448 = (); + badge447 = join453 = 18446744071585419022i32; + Uncle122 { + turn126: badge447; + distribution123: achiever448; + station124: substance449; + fan129: 485754028709818203u64; + snow125: 18446744073709551565i8; + orange127: good-bye450; + run128: collar451; + coal130: observation452; + }.coal130 +})); +seed427 = 28526i16; +earth428 = 18446744073709524978i16; +base438 = move439 = { + beast440 = 4175861882184496300u64; + beast440 +}; +appliance429 = base438; +building430 = 231u8; +function amusement432(poison434: u64,disgust435: i16,process436: u16) -> struct {sister54: struct {scarf59: i16; +grip61: i16; +change60: _; +song57: u64; +stamp58: _; +nerve55: i16; +lamp62: _; +trade63: void; +sponge64: _; +downtown65: u16; +breath56: _;}; +cart68: i16; +van53: u16; +guitar66: u32; +ghost67: _;} { + print 16328509772765407766i64; + 4363529904802166077i64; + waste437 = 52899u16 +}.adjustment52; +crow431 = amusement432((208u8 + 7786906552691021673u64),18446744073709546738i16,canvas433 = 10328u16); +function governor335(beggar386: u16,wash387: i32,kite388: i32) -> Cable92 Cable92 { + patch97: { + curtain402 = 9i8; + wave401 = curtain402; + meat390 = wave401; + measure399 = system400(269712784u32); + spot391 = throat398(12115967182728398125i64,measure399,63578u16,kite388,beggar386); + notebook392 = { + science397 = void(); + ants396 = science397; + stop395 = ants396; + stop395 + }; + veil394 = 251u8; + screw393 = veil394; + snails389 = Uncle122 { + turn126: kite388; + distribution123: meat390; + station124: 1425248044u32; + fan129: spot391; + snow125: notebook392; + orange127: 18446744073709522584i16; + run128: screw393; + coal130: void(); + }; + snails389.coal130 + }; + lock98: { + wave405 = 18446744073709551517i8; + bed403 = summer404 = wave405; + print bed403 + }; + popcorn93: Cable92 { + patch97: kite388; + lock98: 7i8; + popcorn93: 10040507472757699327i64; + hydrant95: star406; + summer94: oil407; + coast99: Judge45 { + dust50: smoke408; + pleasure69: 4282388069u32; + babies48: 235u8; + icicle46: children409; + tray49: 207u8; + flavor70: pan410; + stew71: 794614811i32; + order47: dime411; + flame51: bedroom412; + adjustment52: meal413; + }; + cord96: 12906i16; + }.popcorn93; + hydrant95: Giraffe251 { + cart255: question414; + coach253: 1651700287i32; + company252: 11501i16; + pigs256: 15313944485452575103i64; + meeting254: kite388; + thread257: 28561u16; + boys258: Ground237 { + baby241: 25273u16; + fly247: roof415; + pin244: 2170181168762719532u64; + join239: sticks416; + joke243: girl417; + lace240: 68i8; + art245: 45336u16; + quartz238: 356166077i32; + banana242: brothers418; + achieve246: seashore419; + hands248: 16153885896939426455u64; + quilt249: 11229564187573826941i64; + transport250: Uncle122 { + turn126: 1129505830i32; + distribution123: 18446744073709551495i8; + station124: 1186571215u32; + fan129: 15416858606501447447u64; + snow125: jelly420; + orange127: camp421; + run128: lead422; + coal130: drawer423; + }; + }; + }.pigs256; + summer94: Cable92 { + patch97: 18446744071843451436i32; + lock98: bat424; + popcorn93: 6598612451974632182i64; + hydrant95: 4056111845337930730i64; + summer94: impulse425; + coast99: crow426; + cord96: seed427; + }.summer94; + coast99: Judge45 { + dust50: earth428; + pleasure69: appliance429; + babies48: 198u8; + icicle46: 18446744073709547042i16; + tray49: building430; + flavor70: 18446744073709551600i8; + stew71: wash387; + order47: 116i8; + flame51: 18446744073709550770i16; + adjustment52: crow431; + }; + cord96: 18446744073709521362i16; +}; +struct Tree348 { + vessel349: u16; + stage350: u64; + worm351: u16; + chalk352; + robin353: Sand141; +} +struct balloon355 { + grip61: i16; + sponge64: i16; + lamp62: i64; + change60; + downtown65: u16; + nerve55: i16; + trade63; + song57; + breath56: i64; + scarf59: i16; + stamp58; +} +stomach375 = 11578225168662549964u64; +function ball377(cherry379: u16) -> Ground237 { + fire385 = void(); + building384 = fire385; + baseball383 = building384; + selection382 = baseball383; + crate381 = selection382; + fire380 = 18446744073709523152i16; + fire380; + crate381 +}; +function grip374(throne376: u64) -> Ground237 ball377(support378 = 59084u16); +function teaching371(camp372: i8,waves373: u32) -> Ground237 grip374(stomach375); +function doll357(creature368: u64,church369: u8,drink370: u32) -> u64 (teaching371(101i8,drink370).joke243 * 4654734029517132497u64); +struct grandmother345 { + blood339: u16; + drop341: u64; + parcel340: u8; + reason342; +} +struct grandfather146 { + nerve55: i16; + sponge64; + grip61: i16; + breath56: i64; + change60: i32; + lamp62: u64; + downtown65: u16; + scarf59: i16; + song57: u64; + stamp58: i8; + trade63: void; +} +struct dogs143 { + scarf59: i16; + trade63: void; + grip61; + lamp62: u16; + song57: u64; + sponge64; + nerve55: i16; + downtown65: u16; + change60: u64; + stamp58: u16; + breath56: i64; +} +struct news364 { + stamp58: u64; + trade63; + nerve55; + grip61: i16; + lamp62: u8; + change60: u64; + scarf59: i16; + downtown65: u16; + sponge64; + breath56; + song57: u64; +} +struct Cable92 { + patch97: i32; + lock98: i8; + popcorn93; + hydrant95: i64; + summer94: u8; + coast99: Judge45; + cord96: i16; +} +{ + plot100 = 18446744073709532481i16; + curve101 = sea286; + queen102 = pie367 = park118; + mark366 = (); + degree363 = mark366; + connection360 = distance361 = taste362 = Uncle122 { + turn126: 18446744072218951706i32; + distribution123: degree363.lace240; + station124: Sand141 { + sister54: news364 { + stamp58: 7053964142016805199u64; + trade63: sneeze303; + nerve55: 3235i16; + grip61: airport180; + lamp62: park118; + change60: hand330; + scarf59: 18446744073709534314i16; + downtown65: 53241u16; + sponge64: tooth230; + breath56: quartz179; + song57: 16536171368866367293u64; + }; + cart68: low198; + van53: 9759u16; + guitar66: 3295012601u32; + ghost67: holiday175; + }.guitar66; + fan129: rod365 = 8438326883198311588u64; + snow125: minute184; + orange127: 109i8; + run128: North85 { + day87: 18446744073547546958i32; + brake86: debt283; + apple88: position336; + }.brake86; + coal130: print 18446744072939991794i32; + }; + error103 = (connection360.distribution123 + 18446744073709551526i8); + quicksand104 = 148920358i32; + paper144 = temper359 = { + 7881284048282624926i64; + 18446744073709541756i16 + }; + pickle145 = 446809432i32; + activity338 = { + mountain354 = Sand141 { + sister54: balloon355 { + grip61: donkey356 = 8733i16; + sponge64: 22071i16; + lamp62: (9033855612253305908i64 + 15404753414117902284i64); + change60: doll357(17223145691092374726u64,park118,2559317788u32); + downtown65: 2448u16; + nerve55: 18446744073709523722i16; + trade63: sneeze303; + song57: 59439u16; + breath56: history358 = swim174; + scarf59: { + sneeze303; + 18446744073709551532i16; + 11824046291194271335i64 + }; + stamp58: 105u8; + }; + cart68: 30564i16; + van53: 47246u16; + guitar66: 2706534672u32; + ghost67: 2014052092263007775i64; + }; + brick347 = Tree348 { + vessel349: plantation196; + stage350: celery215; + worm351: 16737u16; + chalk352: bomb333; + robin353: mountain354; + }.robin353; + bead343 = chalk344 = grandmother345 { + blood339: punishment156; + drop341: cattle159; + parcel340: eggnog204; + reason342: void(); + }; + ((play346 = swim174 - 4324112170374473498i64) / brick347.ghost67) + }; + kittens337 = activity338; + request334 = kittens337; + camp147 = (((4026471157407651049i64 * swim174) * Ground237 { + baby241: 54387u16; + fly247: kitty160; + pin244: 5428300011443108695u64; + join239: kitty160; + joke243: representative307; + lace240: bedroom177; + art245: 34598u16; + quartz238: 18446744073593239547i32; + banana242: holiday175; + achieve246: swim174; + hands248: cattle159; + quilt249: 6837605806807892212i64; + transport250: request334; + }.achieve246) / governor335(position336 = punishment156,sea286,1643937940i32).popcorn93); + class148 = sea286; + snakes149 = bomb333 = 18446744072730550875i32; + amount150 = neck328(jellyfish329 = hand330 = cattle159,{ + friends331 = { + holiday175; + void(); + 4233970774428340443u64 + }; + void(); + Sand141 { + sister54: friends331; + cart68: 10943i16; + van53: 64857u16; + guitar66: kitty160; + ghost67: holiday175; + }.ghost67; + flight332 = { + void(); + punishment156 + } + }); + snow151 = { + hydrant323 = { + dime325 = kitty326 = Uncle122 { + turn126: payment231; + distribution123: 18446744073709551506i8; + station124: step306; + fan129: 18413257637346962374u64; + snow125: 18446744073709551519i8; + orange127: low198; + run128: 18446744073709539583i16; + coal130: sneeze303; + }; + weight324 = dime325; + weight324.coal130; + plough327 = kitty326.coal130 + }; + 18446744073709551581i8; + hydrant323.answer322 + }; + tooth152 = 37615u16; + way304 = Uncle122 { + turn126: ({ + { + sneeze303; + minute184 + }; + void(); + void(); + attempt305 = minute184 + } - 18446744072358290916i32); + distribution123: 18446744073709551592i8; + station124: step306 = sneeze303; + fan129: representative307 = cattle159; + snow125: { + 8852111957485383986i64; + void() + }.flavor70; + orange127: (North85 { + day87: payment231; + brake86: 18446744073709537995i16; + apple88: plantation196; + }.brake86 / ({ + park118; + sneeze303; + punishment156; + sneeze303; + 18446744073709551589i8; + achieve214 + } - 18446744073709549090i16)); + run128: pain308().cart68; + coal130: { + work309 = { + sneeze303; + sneeze303; + void() + }; + 7137758502143692695u64; + sneeze303 + }; + }; + knife153 = way304.distribution123; + trousers154 = sneeze303 = void(); + detail155 = debt283; + throne289 = pail290(23367u16,{ + women295 = Uncle122 { + turn126: 18446744072598915349i32; + distribution123: blow296 = bedroom177; + station124: popcorn297 = memory73; + fan129: park118; + snow125: { + void(); + void(); + 41426u16; + 18446744073709549892i16; + balance291 + }; + orange127: beef285; + run128: beef285; + coal130: void(); + }; + alarm293 = Uncle122 { + turn126: payment231; + distribution123: 18446744073709551583i8; + station124: kitty160; + fan129: 17459195364411202387u64; + snow125: 18446744073709551546i8; + orange127: beef285; + run128: 230i16; + coal130: void(); + }.coal130; + balance291 = desk292 = alarm293; + 2392361910u32; + { + (bedroom177 + 18446744073709551567i8); + void(); + void() + }; + print yoke294 = 13065724718737136134i64; + women295.coal130 + },{ + power298 = void(); + way299(memory73,{ + power298; + children194; + debt283; + 18446744073709536365i16; + 18446744073709551517i8; + 19806u16 + },dog300 = plantation196); + heart301(Judge45 { + dust50: 18446744073709523459i16; + pleasure69: kitty160; + babies48: 168u8; + icicle46: 30599i16; + tray49: 59u8; + flavor70: 18446744073709551582i8; + stew71: payment231; + order47: minute184; + flame51: 18446744073709529563i16; + adjustment52: Sand141 { + sister54: anger302 { + grip61: 6879i16; + scarf59: debt283; + sponge64: 17088854891809375404u64; + song57: 17382763896635776588u64; + breath56: memory73; + stamp58: swim174; + change60: 14027642651348983091i64; + trade63: void(); + nerve55: tooth230; + lamp62: park118; + downtown65: 18902u16; + }; + cart68: sea286; + van53: 55940u16; + guitar66: 1937374913u32; + ghost67: 10571996978304881236i64; + }; + }.flavor70,{ + holiday175; + power298 + },Cable92 { + patch97: payment231; + lock98: 12i8; + popcorn93: swim174; + hydrant95: swim174; + summer94: 149u8; + coast99: flower91; + cord96: 18446744073709533603i16; + }.cord96,281585653u32); + 16102167296780401604i64 + },174u8); + quill288 = throne289.boys258.transport250; + answer157 = quill288; + clouds161 = oven287 = void(); + goat162 = sea286 = 3363i16; + creator163 = low198; + rock164 = (debt283 = Sand141 { + sister54: money284 { + grip61: 19604i16; + downtown65: 57373u16; + breath56: flower91; + sponge64: 19828u16; + stamp58: holiday175; + trade63: void(); + change60: 762717406635380723i64; + nerve55: tooth230; + lamp62: 1511418300027772757u64; + scarf59: 18446744073709548827i16; + song57: 4672560773876621133u64; + }; + cart68: tooth230; + van53: 52050u16; + guitar66: 1470183283u32; + ghost67: 7902346666730433854i64; + }.cart68 * beef285 = 7801i16); + decision165 = 71u8; + purpose166 = 124i8; + song277 = Cable92 { + patch97: payment231; + lock98: { + ray278 = tent279 = void(); + ray278 + }; + popcorn93: swim174; + hydrant95: 3087317905768377841i64; + summer94: park118; + coast99: { + doll280 = smile281(Cable92 { + patch97: payment231; + lock98: 18446744073709551502i8; + popcorn93: 8663565394248936019i64; + hydrant95: swim174; + summer94: achieve214; + coast99: flower91; + cord96: tooth230; + }.cord96,4594i16,void()); + (memory73 * { + void(); + void() + }); + 18446744073709529428i16; + doll280.coal130 + }; + cord96: day282(punishment156,(49149u16 - 18688u16),151u8); + }; + look167 = song277.cord96; + fan169 = 18446744073709551585i8; + police170 = { + 15742911005460567467i64; + girls265 = (children194 * (6815440042364331342u64 * cattle159)); + furniture266 = (5196u16 * { + twist268 = void(); + detail267 = twist268; + detail267 + }); + bedroom177; + { + change269 = fear270 = void(); + change269; + void() + }.boys258; + void(); + 4499466857048795752u64; + { + cakes274 = creature275(advice276 = 47068u16); + humor273 = cakes274.boys258; + scarecrow272 = Giraffe251 { + cart255: 18446744073709526231i16; + coach253: payment231; + company252: 27910i16; + pigs256: 8999915976299805012i64; + meeting254: payment231; + thread257: 51598u16; + boys258: humor273; + }.boys258; + anger271(8739277478197002817u64).turn126; + Giraffe251 { + cart255: low198; + coach253: payment231; + company252: 2747i16; + pigs256: 18374677245360721175i64; + meeting254: payment231; + thread257: furniture266; + boys258: scarecrow272; + }.boys258.banana242 + } + }; + nest181 = { + void(); + 25114u16; + 33777u16 + }; + drawer189 = { + pollution260 = fireman261({ + { + brothers263 = void(); + playground262 = brothers263; + holiday175; + void(); + 17399u16; + playground262 + }; + void(); + balls264(9439i16) + },7830885143521923965u64); + print pollution260.coal130 + }; + clock197 = war234(18446744073709521112i16,{ + zebra259 = Uncle122 { + turn126: payment231; + distribution123: 18446744073709551594i8; + station124: 420866508u32; + fan129: 343467285536515312u64; + snow125: quartz179; + orange127: 11705i16; + run128: airport180; + coal130: void(); + }.coal130; + dock236 = Giraffe251 { + cart255: low198; + coach253: 18446744071994992827i32; + company252: 18446744073709527573i16; + pigs256: swim174; + meeting254: 1986863291i32; + thread257: punishment156; + boys258: Ground237 { + baby241: 64330u16; + fly247: 45877588u32; + pin244: children194; + join239: 2808292678u32; + joke243: children194; + lace240: 18446744073709551590i8; + art245: punishment156; + quartz238: payment231; + banana242: swim174; + achieve246: 12934701484959187900i64; + hands248: 11350059803925834449u64; + quilt249: 13091402719820943966i64; + transport250: Uncle122 { + turn126: 891587001i32; + distribution123: quartz179; + station124: kitty160; + fan129: cattle159; + snow125: 2i8; + orange127: low198; + run128: 5891i16; + coal130: zebra259; + }; + }; + }.boys258.transport250; + vein235 = Uncle122 { + turn126: 18446744072937901225i32; + distribution123: 18446744073709551514i8; + station124: memory73; + fan129: 11127243054885810068u64; + snow125: (minute184 + quartz179); + orange127: (); + run128: low198; + coal130: dock236.coal130; + }; + (quartz179 + vein235.distribution123) + }); + chair206 = exchange233(punishment156); + trains232 = payment231; + toy207 = payment231 = trains232; + cry228 = women229(swim174,tooth230 = (airport180 * airport180),16u8,22445u16,park118).day87; + vacation208 = cry228; + respect209 = { + wealth226 = corn227(()); + sofa225 = wealth226; + blade220 = heart223 = journey224(Judge45 { + dust50: 18446744073709541347i16; + pleasure69: 1472977443u32; + babies48: 33u8; + icicle46: low198; + tray49: 161u8; + flavor70: bedroom177; + stew71: 18446744073011864530i32; + order47: minute184; + flame51: 3817i16; + adjustment52: sofa225; + }.icicle46,41305u16); + girl216 = dolls222({ + 14513155067190388261i64; + 18050756703487386890i64 + },8202878863691520212u64); + void(); + Uncle122 { + turn126: girl216; + distribution123: language217(cattle159,18446744072831661146i32,plantation196); + station124: goat218 = 2804789239u32; + fan129: sheep219 = 10430559752008259462u64; + snow125: bedroom177; + orange127: blade220.orange127; + run128: 18446744073709541726i16; + coal130: lawyer221 = void(); + }.coal130 + }; + corn193 = Picture29 { + start30: children194 = 16680978786432417125u64; + girls31: number195 { + father9: plantation196 = punishment156; + fight15: clock197; + size10: low198 = { + health203 = void(); + smash202 = health203; + thing201 = smash202; + light200 = thing201; + dinosaurs199 = void(); + dinosaurs199; + light200 + }; + poison2: (934104573u32 * memory73); + pickle12: eggnog204 = (park118 / 48u8); + watch7: hate205((chair206 + toy207)); + trip3: Uncle122 { + turn126: vacation208; + distribution123: 18446744073709551599i8; + station124: memory73; + fan129: 9037075441710387723u64; + snow125: minute184; + orange127: 18661i16; + run128: 18446744073709548992i16; + coal130: respect209; + }.fan129; + disease4: 1666402167i32; + crown13: { + agreement210 = void(); + 18446744073088148500i32; + void() + }; + direction11: { + event213 = void(); + discovery212 = event213; + writer211 = discovery212; + cattle159; + 16514286589377468965u64; + writer211 + }.popcorn93; + drop14: ().apple88; + earthquake8: achieve214 = (eggnog204 - 223u8); + pizzas6: 1475464825u32; + twist5: celery215 = memory73; + temper28: 4i8.ghost67; + }; + }; + stamp191 = bear192 = corn193; + bomb185 = Sand141 { + sister54: drawer189; + cart68: 18446744073709528180i16; + van53: punishment156; + guitar66: desire190(stamp191.start30); + ghost67: punishment156; + }; + cakes171 = Judge45 { + dust50: airport180; + pleasure69: (kitty160 - 622102885u32); + babies48: 4u8; + icicle46: 18446744073709528663i16; + tray49: 55u8; + flavor70: Uncle122 { + turn126: nest181; + distribution123: quartz179; + station124: 3693348283u32; + fan129: cattle159; + snow125: 18446744073709551595i8; + orange127: airport180; + run128: airport180; + coal130: void(); + }.distribution123; + stew71: { + power183 = void(); + zipper182 = power183; + void(); + zipper182; + punishment156 + }; + order47: minute184 = 109i8; + flame51: bomb185.cart68; + adjustment52: { + clocks188 = void(); + ground187 = clocks188; + crack186 = ground187; + crack186 + }; + }.flavor70; + start172 = airport180 = { + 8535126881572487324i64; + { + 1266752838u32; + bedroom177 + } + }.cart68; + riddle173 = 2481729280032566719i64; + throne178 = quartz179 = 39i8; + kiss176 = bedroom177 = (42i8 + throne178); + mailbox142 = Sand141 { + sister54: dogs143 { + scarf59: (18446744073709550578i16 / Judge45 { + dust50: 18446744073709522366i16; + pleasure69: memory73; + babies48: park118; + icicle46: 1548i16; + tray49: park118; + flavor70: paper144; + stew71: pickle145; + order47: 66i8; + flame51: 13444i16; + adjustment52: Sand141 { + sister54: grandfather146 { + nerve55: 10159i16; + sponge64: camp147; + grip61: class148; + breath56: 12976477981836746741i64; + change60: snakes149; + lamp62: 9470861370444762325u64; + downtown65: amount150; + scarf59: 29424i16; + song57: 9596183082550025477u64; + stamp58: 105i8; + trade63: void(); + }; + cart68: snow151; + van53: tooth152; + guitar66: memory73; + ghost67: knife153; + }; + }.icicle46); + trade63: trousers154; + grip61: detail155; + lamp62: punishment156 = 42742u16; + song57: 12987453853705528850u64; + sponge64: answer157; + nerve55: 18446744073709534218i16; + downtown65: knot158(18446744072360701016i32).van53; + change60: cattle159 = punishment156; + stamp58: punishment156; + breath56: { + void(); + void(); + void() + }; + }; + cart68: 17357u16.brake86; + van53: punishment156; + guitar66: kitty160 = (Uncle122 { + turn126: 18446744072405837675i32; + distribution123: clouds161; + station124: 1160807849u32; + fan129: 2875659560902941949u64; + snow125: 18446744073709551554i8; + orange127: goat162; + run128: creator163; + coal130: void(); + }.station124 * Judge45 { + dust50: 18446744073709546504i16; + pleasure69: memory73; + babies48: 156u8; + icicle46: rock164; + tray49: park118; + flavor70: 18446744073709551546i8; + stew71: decision165; + order47: purpose166; + flame51: look167; + adjustment52: Sand141 { + sister54: credit168 { + grip61: 554i16; + sponge64: 51701u16; + breath56: 59453u16; + scarf59: 27326i16; + song57: 949247836507273360u64; + stamp58: 12966u16; + lamp62: 18446744073709533164i16; + change60: fan169; + downtown65: punishment156; + trade63: police170; + nerve55: cakes171; + }; + cart68: start172; + van53: punishment156; + guitar66: 2993101503u32; + ghost67: riddle173; + }; + }.pleasure69); + ghost67: swim174 = holiday175 = kiss176; + }; + mark105 = mailbox142; + van140 = 18446744073709521705i16; + scent106 = van140; + guide117 = park118; + noise107 = apples113({ + cream115 = women116 = dog114; + void(); + dog114 = cream115; + 17209141515412734178i64; + void(); + women116 + },guide117,park118 = 122u8,3337030618u32,{ + voice131 = { + ants135 = 30243u16; + curve134 = ants135; + straw133 = curve134; + ladybug132 = 8951960167217801009u64; + 1849341856u32; + ladybug132; + straw133 + }; + calculator121 = voice131.coal130; + chair119 = humor120 = calculator121; + 3291644205u32; + chair119 + },{ + rainstorm138 = 1294770465i32; + condition137 = void(); + void(); + chickens136 = 889625268165421120u64; + condition137; + (rainstorm138 * 18446744073641559020i32) + },(),boat139(18446744073709521205i16)).apple88; + bottle108 = 4738617239359698449i64; + sink75 = flower91 = Cable92 { + patch97: Cable92 { + patch97: 18446744072768037647i32; + lock98: 18446744073709551489i8; + popcorn93: 4904773328637654342i64; + hydrant95: 11800843184655466469i64; + summer94: 173u8; + coast99: Judge45 { + dust50: plot100; + pleasure69: memory73; + babies48: 71u8; + icicle46: curve101; + tray49: queen102; + flavor70: error103; + stew71: quicksand104; + order47: 99i8; + flame51: 18446744073709540830i16; + adjustment52: mark105; + }; + cord96: scent106; + }.patch97; + lock98: 18446744073709551527i8; + popcorn93: noise107; + hydrant95: bottle108; + summer94: 161u8; + coast99: { + bulb112 = 15435183071503823543u64; + recess111 = bulb112; + toe110 = recess111; + crack109 = toe110; + crack109 + }; + cord96: 18446744073709548188i16; + }.coast99; + furniture74 = { + scissors84 = 18446744072808699951i32; + needle90 = 9101443493892900697u64; + show89 = needle90; + growth81 = throne82(receipt83 = scissors84,show89.apple88); + joke78 = account79 = hall80 = growth81; + bell77 = joke78; + donkey76 = bell77; + donkey76 + }; + { + banana1 = 9256826831721285518u64; + lake0 = banana1; + void(); + lake0 + }; + { + print { + plantation36 = void(); + sugar35 = plantation36; + comparison34 = sugar35; + purpose33 = comparison34; + force32 = purpose33; + void(); + void(); + force32 + }.girls31; + { + { + eggnog41 = 46i8; + branch40 = eggnog41; + wool39 = branch40; + kite38 = wool39; + talk37 = 64398u16; + talk37; + kite38 + }; + void(); + process42 = 16172324216128316233i64 + }; + void() + }; + { + void(); + north43 = jelly44 = 2535350545u32.pleasure69; + { + (18446744073709543937i16 + 22875i16); + 18446744073709551610i8; + deer72 = 18446744073709550697i16 + } + }; + (memory73 = furniture74 / sink75.pleasure69) +}; + + diff --git a/src/bin/gen_program.rs b/src/bin/gen-program.rs similarity index 100% rename from src/bin/gen_program.rs rename to src/bin/gen-program.rs diff --git a/src/ir/arbitrary.rs b/src/ir/arbitrary.rs index 16f8b99..fd70909 100644 --- a/src/ir/arbitrary.rs +++ b/src/ir/arbitrary.rs @@ -180,9 +180,9 @@ pub struct ProgramTree { } impl ProgramTree { - fn new(mut rng: TestRng) -> Self { + fn new(_rng: TestRng) -> Self { // let mut items = vec![]; - let program_length = PROGRAM_LENGTH_DISTRIBUTION.sample(&mut rng); + //let program_length = PROGRAM_LENGTH_DISTRIBUTION.sample(&mut rng); // let mut env = ScopedMap::new(); // for _ in 0..program_length { @@ -216,10 +216,10 @@ impl ProgramTree { let current = Program { functions: HashMap::new(), type_definitions: HashMap::new(), - body: unimplemented!(), + body: Expression::Block(Location::manufactured(), Type::void(), vec![]), }; - ProgramTree { _rng: rng, current } + ProgramTree { _rng, current } } } @@ -459,8 +459,8 @@ fn generate_random_name(rng: &mut TestRng) -> Name { Name::gensym(start) } -fn generate_random_argument_type(rng: &mut TestRng) -> Type { - ARGUMENT_TYPE_FREQUENCIES[ARGUMENT_TYPE_DISTRIBUTION.sample(rng)] - .0 - .clone() -} +//fn generate_random_argument_type(rng: &mut TestRng) -> Type { +// ARGUMENT_TYPE_FREQUENCIES[ARGUMENT_TYPE_DISTRIBUTION.sample(rng)] +// .0 +// .clone() +//} diff --git a/src/syntax.rs b/src/syntax.rs index dbbc97b..2f638ef 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -322,17 +322,38 @@ fn order_of_operations() { } proptest::proptest! { - #[test] - fn random_syntaxes_validate(program in self::arbitrary::ProgramGenerator::default()) { - let result = Program::validate(program); - proptest::prop_assert!(result.is_ok()); - } + #[test] + fn syntax_asts_roundtrip(program in self::arbitrary::ProgramGenerator::default()) { + use crate::util::pretty::Allocator; - #[test] - fn generated_run_or_overflow(program in self::arbitrary::ProgramGenerator::default()) { - use crate::eval::{EvalError, PrimOpError}; - let validated = Program::validate(program); - let actual_program = validated.into_result().expect("got a valid result"); - proptest::prop_assert!(matches!(actual_program.eval(), Ok(_) | Err(EvalError::PrimOp(PrimOpError::MathFailure(_))))); - } + let allocator = Allocator::new(); + let mut outbytes = Vec::new(); + + for top_level in program.iter() { + let docbuilder = top_level.pretty(&allocator); + docbuilder.render(78, &mut outbytes).expect("can write program text"); + } + + let outstr = std::str::from_utf8(&outbytes).expect("generated utf8"); + println!("---------------- GENERATED TEXT -------------------"); + println!("{}", outstr); + println!("---------------------------------------------------"); + + let syntax = crate::syntax::parse_string(0, &outstr).expect("generated text parses"); + assert_eq!(program, syntax); + } + + #[test] + fn random_syntaxes_validate(program in self::arbitrary::ProgramGenerator::default()) { + let result = Program::validate(program); + proptest::prop_assert!(result.is_ok()); + } + + #[test] + fn generated_run_or_overflow(program in self::arbitrary::ProgramGenerator::default()) { + use crate::eval::{EvalError, PrimOpError}; + let validated = Program::validate(program); + let actual_program = validated.into_result().expect("got a valid result"); + proptest::prop_assert!(matches!(actual_program.eval(), Ok(_) | Err(EvalError::PrimOp(PrimOpError::MathFailure(_))))); + } } diff --git a/src/syntax/arbitrary.rs b/src/syntax/arbitrary.rs index 5edc698..061c6b1 100644 --- a/src/syntax/arbitrary.rs +++ b/src/syntax/arbitrary.rs @@ -18,21 +18,22 @@ const MAX_COMPLEX_DEPTH: usize = 4; lazy_static::lazy_static! { static ref BASE_PROGRAM_LENGTH: WeightedIndex = WeightedIndex::new([ 0, // weight for 0 - 1, // weight for 1 - 1, // weight for 2 - 1, // weight for 3 - 3, // triple weight for 4 - 3, // triple weight for 5 - 3, // triple weight for 6 - 3, // triple weight for 7 - 3, // triple weight for 8 - 3, // triple weight for 9 - 3, // triple weight for 10 - 3, // double weight for 11 - 3, // double weight for 12 - 3, // double weight for 13 - 1, // weight for 14 - 1, // weight for 15 + 9, +// 1, // weight for 1 +// 1, // weight for 2 +// 1, // weight for 3 +// 3, // triple weight for 4 +// 3, // triple weight for 5 +// 3, // triple weight for 6 +// 3, // triple weight for 7 +// 3, // triple weight for 8 +// 3, // triple weight for 9 +// 3, // triple weight for 10 +// 3, // double weight for 11 +// 3, // double weight for 12 +// 3, // double weight for 13 +// 1, // weight for 14 +// 1, // weight for 15 ]).unwrap(); static ref KEEP_FIELD_TYPE_ANNOTATION: WeightedMap = WeightedMap::new(&[ @@ -151,7 +152,7 @@ impl Strategy for ProgramGenerator { pub struct ProgramTree { _rng: TestRng, - current: VecDeque, + current: Vec, } pub enum Requirement { @@ -167,14 +168,15 @@ impl ProgramTree { let mut rng = runner.new_rng(); let base_program_length = BASE_PROGRAM_LENGTH.sample(&mut rng); let mut env = ScopedMap::new(); - let mut current = VecDeque::new(); + let mut current = Vec::new(); let mut established_structs = HashMap::new(); while current.len() < base_program_length { let (expression, mut requirements) = generate_expr(0, &mut rng, &mut established_structs, &mut env, None); + let mut new_entries = VecDeque::new(); - current.push_front(TopLevel::Expression(expression)); + new_entries.push_back(TopLevel::Expression(expression)); while let Some(requirement) = requirements.pop() { match requirement { Requirement::Function(name, args, result) => { @@ -187,7 +189,7 @@ impl ProgramTree { args, result, ); - current.push_front(TopLevel::Expression(expression)); + new_entries.push_front(TopLevel::Expression(expression)); requirements.extend(newreqs.into_iter()); } @@ -202,7 +204,7 @@ impl ProgramTree { } }) .collect(); - current.push_front(TopLevel::Structure( + new_entries.push_front(TopLevel::Structure( Location::manufactured(), name, fields, @@ -219,17 +221,16 @@ impl ProgramTree { ); let binding = Expression::Binding(Location::manufactured(), name, Box::new(newexpr)); - current.push_front(TopLevel::Expression(binding)); + new_entries.push_front(TopLevel::Expression(binding)); requirements.extend(newreqs.into_iter()); } } } + + current.extend(new_entries.into_iter()); } - ProgramTree { - _rng: rng, - current: current.into_iter().collect(), - } + ProgramTree { _rng: rng, current } } } @@ -287,7 +288,7 @@ fn generate_expr( let mut valid_types = find_structs_with_field_type(established_struct_types, &target_type); if valid_types.is_empty() { - let name = generate_name(rng); + let struct_name = generate_type_name(rng); let mut fields = generate_structure_fields(rng, established_struct_types); valid_types = fields .iter() @@ -298,10 +299,11 @@ fn generate_expr( if valid_types.is_empty() { let field_name = generate_name(rng); fields.insert(field_name.clone(), target_type.clone()); - valid_types = vec![(Type::Named(name.clone()), field_name)]; + valid_types = vec![(Type::Named(struct_name.clone()), field_name)]; } - requirements.push(Requirement::Structure(name.clone(), fields)); + established_struct_types.insert(struct_name.clone(), fields.clone()); + requirements.push(Requirement::Structure(struct_name.clone(), fields)); } let new_target_type_idx = rng.gen_range(0..valid_types.len()); @@ -366,7 +368,7 @@ fn generate_expr( Type::Struct(fields) => match find_struct_for_fields(&fields, established_struct_types) { None => { - let name = generate_name(rng); + let struct_name = generate_type_name(rng); let mut new_fields = HashMap::new(); for (field_name, maybe_type) in fields.into_iter() { @@ -375,20 +377,20 @@ fn generate_expr( new_fields.insert(field_name, field_type); } - established_struct_types.insert(name.clone(), new_fields.clone()); + established_struct_types.insert(struct_name.clone(), new_fields.clone()); let (subexpr, mut reqs) = generate_expr( depth + 1, rng, established_struct_types, env, - Some(Type::Named(name.clone())), + Some(Type::Named(struct_name.clone())), ); let result = Expression::Cast( Location::manufactured(), - name.current_name().to_string(), + struct_name.current_name().to_string(), Box::new(subexpr), ); - reqs.push(Requirement::Structure(name, new_fields)); + reqs.push(Requirement::Structure(struct_name, new_fields)); (result, reqs) } @@ -466,6 +468,7 @@ fn generate_expr( let mut block = VecDeque::new(); let mut prereqs = vec![]; + env.new_scope(); while block.len() < target_block_size_minus_one { let inner_type = if INNER_BLOCK_TYPE_SHOULD_BE_VOID.sample(rng) { Some(Type::Named(Name::new("void", Location::manufactured()))) @@ -501,6 +504,7 @@ fn generate_expr( } } } + env.release_scope(); let retval = Expression::Block(Location::manufactured(), block.into_iter().collect()); (retval, prereqs) @@ -705,19 +709,34 @@ fn find_struct_for_fields( None } -fn generate_name(rng: &mut TestRng) -> Name { +fn internal_generate_name(rng: &mut TestRng, is_type_name: bool) -> Name { static COUNTER: AtomicU64 = AtomicU64::new(0); let idx = rng.gen_range(0..names::NOUNS.len()); + let mut noun = names::NOUNS[idx].to_string(); + + if is_type_name { + let first_char = noun.get_mut(0..1).expect("has first character"); + first_char.make_ascii_uppercase(); + } + let name = format!( "{}{}", - names::NOUNS[idx], + noun, COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst) ); Name::new(name, Location::manufactured()) } +fn generate_name(rng: &mut TestRng) -> Name { + internal_generate_name(rng, false) +} + +fn generate_type_name(rng: &mut TestRng) -> Name { + internal_generate_name(rng, true) +} + fn generate_type(rng: &mut TestRng, established_struct_types: &mut EstablishedStructMap) -> Type { let possible_result = TYPE_FREQUENCIES.sample(rng); diff --git a/src/syntax/parser.lalrpop b/src/syntax/parser.lalrpop index 6455827..16dba91 100644 --- a/src/syntax/parser.lalrpop +++ b/src/syntax/parser.lalrpop @@ -218,6 +218,8 @@ UnaryExpression: Expression = { Expression::primitive(Location::new(file_idx, l..le), "negate", vec![e]), "<" "> ">" => Expression::Cast(Location::new(file_idx, l..le), v.to_string(), Box::new(e)), + "<" "> ">" => + Expression::Cast(Location::new(file_idx, l..le), v.to_string(), Box::new(e)), CallExpression, } diff --git a/src/syntax/pretty.rs b/src/syntax/pretty.rs index 1d92c8a..35a633b 100644 --- a/src/syntax/pretty.rs +++ b/src/syntax/pretty.rs @@ -28,14 +28,14 @@ impl Program { interior = interior.indent(9); - let start = allocator.text("struct") + let start = allocator + .text("struct") .append(allocator.space()) .append(allocator.text(definition.name.original_name().to_string())) .append(allocator.space()) .append(allocator.text("{")); - let conclusion = allocator.text("}") - .append(allocator.hardline()); + let conclusion = allocator.text("}").append(allocator.hardline()); result = result.append(start.append(interior).append(conclusion)); } @@ -113,7 +113,8 @@ impl TopLevel { .append(type_bit) .append(allocator.text(";")) .append(allocator.hardline()) - })).indent(2) + })) + .indent(2), ) .append(allocator.text("}")) .append(allocator.hardline()), @@ -141,7 +142,7 @@ impl Expression { .append(allocator.text(";")) .append(allocator.hardline()) })) - .indent(2) + .indent(2), ) .append(allocator.text("}")), Expression::Reference(var) => allocator.text(var.to_string()), @@ -158,7 +159,10 @@ impl Expression { let mut args = args.iter().map(|x| x.pretty(allocator)).collect::>(); match fun.as_ref() { - Expression::Primitive(_, name) if ["/", "*", "+", "-"].contains(&name.current_name()) && args.len() == 2 => { + Expression::Primitive(_, name) + if ["/", "*", "+", "-"].contains(&name.current_name()) + && args.len() == 2 => + { let second = args.pop().unwrap(); args.pop() .unwrap() @@ -168,14 +172,21 @@ impl Expression { .append(second) .parens() } - - Expression::Primitive(_, name) if ["negate"].contains(&name.current_name()) && args.len() == 1 => - allocator.text("-").append(args.pop().unwrap()), - Expression::Primitive(_, name) if ["print"].contains(&&name.current_name()) && args.len() == 1 => - allocator.text("print") - .append(allocator.space()) - .append(args.pop().unwrap()), + Expression::Primitive(_, name) + if ["negate"].contains(&name.current_name()) && args.len() == 1 => + { + allocator.text("-").append(args.pop().unwrap()) + } + + Expression::Primitive(_, name) + if ["print"].contains(&&name.current_name()) && args.len() == 1 => + { + allocator + .text("print") + .append(allocator.space()) + .append(args.pop().unwrap()) + } _ => { let comma_sepped_args = allocator.intersperse(args, allocator.text(",")); @@ -273,7 +284,7 @@ impl Value { allocator.text(value_str) } - Value::Void => allocator.text(""), + Value::Void => allocator.text("void()"), } } } @@ -281,7 +292,7 @@ impl Value { fn type_suffix(x: &Option) -> &'static str { match x { None => "", - Some(ConstantType::Void) => "", + Some(ConstantType::Void) => panic!("Should never get a void type suffix."), Some(ConstantType::I8) => "i8", Some(ConstantType::I16) => "i16", Some(ConstantType::I32) => "i32", @@ -314,8 +325,7 @@ impl Type { .append(allocator.text(";")) }), allocator.hardline(), - )) - .braces(), + ).braces()) } } } -- 2.53.0