checkpoint
This commit is contained in:
@@ -159,7 +159,7 @@ impl Backend<ObjectModule> {
|
|||||||
.arg(executable_path)
|
.arg(executable_path)
|
||||||
.output()?;
|
.output()?;
|
||||||
|
|
||||||
if !output.stderr.is_empty() {
|
if !output.status.success() {
|
||||||
return Err(EvalError::Linker(
|
return Err(EvalError::Linker(
|
||||||
std::string::String::from_utf8_lossy(&output.stderr).to_string(),
|
std::string::String::from_utf8_lossy(&output.stderr).to_string(),
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -413,7 +413,10 @@ impl<M: Module> Backend<M> {
|
|||||||
// negative number for us. Which sets the high bits, which makes Cranelift unhappy.
|
// 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,
|
// So first we cast the i8 as u8, to get rid of the whole concept of sign extension,
|
||||||
// and *then* we cast to i64.
|
// 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((
|
Value::I16(_, v) => Ok((
|
||||||
// see above note for the "... as ... as"
|
// see above note for the "... as ... as"
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ impl FromStr for PrimitiveType {
|
|||||||
"u16" => Ok(PrimitiveType::U16),
|
"u16" => Ok(PrimitiveType::U16),
|
||||||
"u32" => Ok(PrimitiveType::U32),
|
"u32" => Ok(PrimitiveType::U32),
|
||||||
"u64" => Ok(PrimitiveType::U64),
|
"u64" => Ok(PrimitiveType::U64),
|
||||||
|
"void" => Ok(PrimitiveType::Void),
|
||||||
_ => Err(UnknownPrimType::UnknownPrimType(s.to_owned())),
|
_ => 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::I32(x)) => Ok(Value::I64(*x as i64)),
|
||||||
(PrimitiveType::I64, Value::I64(x)) => Ok(Value::I64(*x)),
|
(PrimitiveType::I64, Value::I64(x)) => Ok(Value::I64(*x)),
|
||||||
|
|
||||||
|
(PrimitiveType::Void, Value::Void) => Ok(Value::Void),
|
||||||
|
|
||||||
_ => Err(PrimOpError::UnsafeCast {
|
_ => Err(PrimOpError::UnsafeCast {
|
||||||
from: PrimitiveType::try_from(source)?,
|
from: PrimitiveType::try_from(source)?,
|
||||||
to: *self,
|
to: *self,
|
||||||
|
|||||||
@@ -228,9 +228,13 @@ where
|
|||||||
.text("print")
|
.text("print")
|
||||||
.append(allocator.space())
|
.append(allocator.space())
|
||||||
.append(allocator.text(var.as_ref().to_string())),
|
.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())
|
.text(var.as_ref().to_string())
|
||||||
.append(allocator.space())
|
.append(allocator.space())
|
||||||
|
.append(allocator.text(":"))
|
||||||
|
.append(allocator.space())
|
||||||
|
.append(ty.pretty(allocator))
|
||||||
|
.append(allocator.space())
|
||||||
.append(allocator.text("="))
|
.append(allocator.text("="))
|
||||||
.append(allocator.space())
|
.append(allocator.space())
|
||||||
.append(expr.pretty(allocator)),
|
.append(expr.pretty(allocator)),
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ pub enum Expression {
|
|||||||
Reference(Location, String),
|
Reference(Location, String),
|
||||||
Cast(Location, String, Box<Expression>),
|
Cast(Location, String, Box<Expression>),
|
||||||
Primitive(Location, String, Vec<Expression>),
|
Primitive(Location, String, Vec<Expression>),
|
||||||
|
Block(Location, Vec<Statement>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Expression {
|
impl PartialEq for Expression {
|
||||||
@@ -145,6 +146,10 @@ impl PartialEq for Expression {
|
|||||||
Expression::Primitive(_, prim2, args2) => prim1 == prim2 && args1 == args2,
|
Expression::Primitive(_, prim2, args2) => prim1 == prim2 && args1 == args2,
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
|
Expression::Block(_, stmts1) => match other {
|
||||||
|
Expression::Block(_, stmts2) => stmts1 == stmts2,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,13 +35,28 @@ impl Program {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TopLevel::Statement(Statement::Binding(_, name, value)) => {
|
TopLevel::Statement(stmt) => last_result = stmt.eval(&mut stdout, &mut env)?,
|
||||||
let actual_value = value.eval(&env)?;
|
}
|
||||||
env.insert(name.clone().intern(), actual_value);
|
|
||||||
last_result = Value::Void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TopLevel::Statement(Statement::Print(loc, name)) => {
|
Ok((last_result, stdout))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Statement {
|
||||||
|
fn eval(
|
||||||
|
&self,
|
||||||
|
stdout: &mut String,
|
||||||
|
env: &mut ScopedMap<ArcIntern<String>, Value<Expression>>,
|
||||||
|
) -> Result<Value<Expression>, EvalError<Expression>> {
|
||||||
|
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
|
let value = env
|
||||||
.get(&name.clone().intern())
|
.get(&name.clone().intern())
|
||||||
.ok_or_else(|| EvalError::LookupFailed(loc.clone(), name.name.clone()))?;
|
.ok_or_else(|| EvalError::LookupFailed(loc.clone(), name.name.clone()))?;
|
||||||
@@ -52,19 +67,17 @@ impl Program {
|
|||||||
};
|
};
|
||||||
let line = format!("{} = {}\n", name, value);
|
let line = format!("{} = {}\n", name, value);
|
||||||
stdout.push_str(&line);
|
stdout.push_str(&line);
|
||||||
last_result = Value::Void;
|
Ok(Value::Void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((last_result, stdout))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expression {
|
impl Expression {
|
||||||
fn eval(
|
fn eval(
|
||||||
&self,
|
&self,
|
||||||
env: &ScopedMap<ArcIntern<String>, Value<Expression>>,
|
stdout: &mut String,
|
||||||
|
env: &mut ScopedMap<ArcIntern<String>, Value<Expression>>,
|
||||||
) -> Result<Value<Expression>, EvalError<Expression>> {
|
) -> Result<Value<Expression>, EvalError<Expression>> {
|
||||||
match self {
|
match self {
|
||||||
Expression::Value(_, v) => match v {
|
Expression::Value(_, v) => match v {
|
||||||
@@ -90,7 +103,7 @@ impl Expression {
|
|||||||
|
|
||||||
Expression::Cast(_, target, expr) => {
|
Expression::Cast(_, target, expr) => {
|
||||||
let target_type = PrimitiveType::from_str(target)?;
|
let target_type = PrimitiveType::from_str(target)?;
|
||||||
let value = expr.eval(env)?;
|
let value = expr.eval(stdout, env)?;
|
||||||
Ok(target_type.safe_cast(&value)?)
|
Ok(target_type.safe_cast(&value)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,11 +112,21 @@ impl Expression {
|
|||||||
|
|
||||||
for arg in args.iter() {
|
for arg in args.iter() {
|
||||||
// yay, recursion! makes this pretty straightforward
|
// 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)?)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,11 +87,13 @@ OptionalName: Option<Name> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Arguments: Vec<Name> = {
|
Arguments: Vec<Name> = {
|
||||||
<mut args:Arguments> <arg:Argument> => {
|
<mut args:Arguments> "," <arg:Argument> => {
|
||||||
args.push(arg);
|
args.push(arg);
|
||||||
args
|
args
|
||||||
},
|
},
|
||||||
|
|
||||||
|
<arg:Argument> => vec![arg],
|
||||||
|
|
||||||
=> Vec::new(),
|
=> Vec::new(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,7 +216,7 @@ AtomicExpression: Expression = {
|
|||||||
// just a number
|
// just a number
|
||||||
<l: @L> <n:"<num>"> <end: @L> => Expression::Value(Location::new(file_idx, l..end), Value::Number(n.0, n.1, n.2)),
|
<l: @L> <n:"<num>"> <end: @L> => Expression::Value(Location::new(file_idx, l..end), Value::Number(n.0, n.1, n.2)),
|
||||||
// this expression could actually be a block!
|
// this expression could actually be a block!
|
||||||
"{" <stmts:Statements> "}" => unimplemented!(),
|
<s:@L> "{" <stmts:Statements> "}" <e:@L> => Expression::Block(Location::new(file_idx, s..e), stmts),
|
||||||
// finally, let people parenthesize expressions and get back to a
|
// finally, let people parenthesize expressions and get back to a
|
||||||
// lower precedence
|
// lower precedence
|
||||||
"(" <e:Expression> ")" => e,
|
"(" <e:Expression> ")" => e,
|
||||||
|
|||||||
@@ -105,6 +105,25 @@ where
|
|||||||
let comma_sepped_args = allocator.intersperse(args, CommaSep {});
|
let comma_sepped_args = allocator.intersperse(args, CommaSep {});
|
||||||
call.append(comma_sepped_args.parens())
|
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("}"))
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -180,7 +180,10 @@ impl Statement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Expression {
|
impl Expression {
|
||||||
fn validate(&self, variable_map: &ScopedMap<String, Location>) -> (Vec<Error>, Vec<Warning>) {
|
fn validate(
|
||||||
|
&self,
|
||||||
|
variable_map: &mut ScopedMap<String, Location>,
|
||||||
|
) -> (Vec<Error>, Vec<Warning>) {
|
||||||
match self {
|
match self {
|
||||||
Expression::Value(_, _) => (vec![], vec![]),
|
Expression::Value(_, _) => (vec![], vec![]),
|
||||||
Expression::Reference(_, var) if variable_map.contains_key(var) => (vec![], vec![]),
|
Expression::Reference(_, var) if variable_map.contains_key(var) => (vec![], vec![]),
|
||||||
@@ -207,6 +210,19 @@ impl Expression {
|
|||||||
warnings.append(&mut warn);
|
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)
|
(errors, warnings)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,9 +71,10 @@ pub fn convert_top_level(
|
|||||||
args.iter().map(|x| ArcIntern::new(x.to_string())).collect();
|
args.iter().map(|x| ArcIntern::new(x.to_string())).collect();
|
||||||
assert_eq!(argtypes.len(), iargs.len());
|
assert_eq!(argtypes.len(), iargs.len());
|
||||||
let mut function_args = vec![];
|
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());
|
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);
|
let (expr, ty) = convert_expression(expr, constraint_db, renames, bindings);
|
||||||
@@ -150,7 +151,7 @@ fn convert_statement(
|
|||||||
fn convert_expression(
|
fn convert_expression(
|
||||||
expression: syntax::Expression,
|
expression: syntax::Expression,
|
||||||
constraint_db: &mut Vec<Constraint>,
|
constraint_db: &mut Vec<Constraint>,
|
||||||
renames: &ScopedMap<ArcIntern<String>, ArcIntern<String>>,
|
renames: &mut ScopedMap<ArcIntern<String>, ArcIntern<String>>,
|
||||||
bindings: &mut ScopedMap<ArcIntern<String>, ir::TypeOrVar>,
|
bindings: &mut ScopedMap<ArcIntern<String>, ir::TypeOrVar>,
|
||||||
) -> (ir::Expression<ir::TypeOrVar>, ir::TypeOrVar) {
|
) -> (ir::Expression<ir::TypeOrVar>, ir::TypeOrVar) {
|
||||||
match expression {
|
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,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,26 @@ pub fn finalize_program(
|
|||||||
mut program: Program<TypeOrVar>,
|
mut program: Program<TypeOrVar>,
|
||||||
resolutions: &TypeResolutions,
|
resolutions: &TypeResolutions,
|
||||||
) -> Program<Type> {
|
) -> Program<Type> {
|
||||||
|
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 {
|
Program {
|
||||||
items: program
|
items: program
|
||||||
.items
|
.items
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ pub enum Constraint {
|
|||||||
ConstantNumericType(Location, TypeOrVar),
|
ConstantNumericType(Location, TypeOrVar),
|
||||||
/// The two types should be equivalent
|
/// The two types should be equivalent
|
||||||
Equivalent(Location, TypeOrVar, TypeOrVar),
|
Equivalent(Location, TypeOrVar, TypeOrVar),
|
||||||
|
/// The given type can be resolved to something
|
||||||
|
IsSomething(Location, TypeOrVar),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Constraint {
|
impl fmt::Display for Constraint {
|
||||||
@@ -43,6 +45,7 @@ impl fmt::Display for Constraint {
|
|||||||
Constraint::NumericType(_, ty) => write!(f, "NUMERIC {}", ty),
|
Constraint::NumericType(_, ty) => write!(f, "NUMERIC {}", ty),
|
||||||
Constraint::ConstantNumericType(_, ty) => write!(f, "CONST_NUMERIC {}", ty),
|
Constraint::ConstantNumericType(_, ty) => write!(f, "CONST_NUMERIC {}", ty),
|
||||||
Constraint::Equivalent(_, ty, ty2) => write!(f, "EQUIVALENT {} => {}", ty, ty2),
|
Constraint::Equivalent(_, ty, ty2) => write!(f, "EQUIVALENT {} => {}", ty, ty2),
|
||||||
|
Constraint::IsSomething(_, ty) => write!(f, "SOMETHING {}", ty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -213,6 +216,10 @@ impl From<TypeInferenceError> for Diagnostic<usize> {
|
|||||||
prim
|
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 resolutions = HashMap::new();
|
||||||
let mut changed_something = true;
|
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
|
// 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
|
// constraints. Internal to the loop, we have a check that will make sure that we
|
||||||
// do (eventually) stop.
|
// do (eventually) stop.
|
||||||
@@ -275,6 +287,16 @@ pub fn solve_constraints(
|
|||||||
// Currently, all of our types are printable
|
// Currently, all of our types are printable
|
||||||
Constraint::Printable(_loc, _ty) => changed_something = true,
|
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
|
// 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
|
// constraint! We can just continue. If they're not equal, add an error and then
|
||||||
// see what else we come up with.
|
// see what else we come up with.
|
||||||
|
|||||||
Reference in New Issue
Block a user