More work on UIR

main
Alex Bethel 2022-08-27 22:16:11 -06:00
parent 4480a1973a
commit 2607722686
5 changed files with 69 additions and 52 deletions

View File

@ -4,8 +4,10 @@
//! simplifies the complexity of the syntax tree, and is used to perform type inference, at which
//! point it is translated into typed IR.
use std::fmt::Display;
use crate::{
syntax::{self, Identifier, SyntaxTree},
syntax::{self, Identifier, Literal, SyntaxTree},
typeck::Type,
};
@ -143,6 +145,19 @@ enum Location {
/// A compiler-generated temporary location.
Temporary(u64),
/// A constant value.
Literal(Literal),
}
impl Display for Location {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Location::Named(id) => write!(f, "n`{}`", id),
Location::Temporary(t) => write!(f, "t`{}`", t),
Location::Literal(l) => write!(f, "c`{}`", l),
}
}
}
/// Converts a program's abstract syntax tree into untyped IR code.
@ -204,7 +219,13 @@ fn convert_fn(
mut arguments: Vec<syntax::Pattern>,
definition: syntax::Expr,
) -> Vec<Instruction> {
if arguments.len() > 1 {
if arguments.is_empty() {
let ret_loc = temporary(counter);
eval_expr(counter, definition, &ret_loc)
.into_iter()
.chain(std::iter::once(Instruction::Return { target: ret_loc }))
.collect()
} else {
let first = arguments.remove(0);
let lambda_loc = temporary(counter);
@ -220,12 +241,6 @@ fn convert_fn(
},
Instruction::Return { target: lambda_loc },
]
} else {
let ret_loc = temporary(counter);
eval_expr(counter, definition, &ret_loc)
.into_iter()
.chain(std::iter::once(Instruction::Return { target: ret_loc }))
.collect()
}
}
@ -271,11 +286,6 @@ fn bind_pattern(counter: &mut u64, p: syntax::Pattern, l: &Location) -> Vec<Inst
/// Emits intructions that evaluate the expression `e`, then place the result in location `l`.
fn eval_expr(counter: &mut u64, e: syntax::Expr, l: &Location) -> Vec<Instruction> {
match e {
syntax::Expr::UnaryOp {
kind,
val,
translation,
} => todo!(),
syntax::Expr::BinaryOp {
kind,
left,
@ -315,7 +325,13 @@ fn eval_expr(counter: &mut u64, e: syntax::Expr, l: &Location) -> Vec<Instructio
}))
.collect()
}
syntax::Expr::VariableReference(_) => todo!(),
syntax::Expr::Literal(_) => todo!(),
syntax::Expr::VariableReference(name) => vec![Instruction::Collect {
target: l.clone(),
source: vec![Location::Named(name)],
}],
syntax::Expr::Literal(lit) => vec![Instruction::Collect {
target: l.clone(),
source: vec![Location::Literal(lit)],
}],
}
}

View File

@ -4,8 +4,8 @@ use std::{error::Error, fmt::Display, process::exit, str::FromStr};
use clap::Parser;
use drimc_rs::{
ir_untyped::ast_to_untyped_ir,
parser::{parser, ParserError, ParserMeta},
typeck::typeck, ir_untyped::ast_to_untyped_ir,
};
/// Optimization levels.
@ -98,6 +98,10 @@ impl FromStr for Optimization {
/// Targets for compiling to.
#[derive(Debug)]
enum Target {
// -------------------- IR generation --------------------
/// Generate untyped intermediate representation (UIR) code.
UIR,
// -------------------- C generation --------------------
// Highest priority codegen, since it allows us to compile to the vast majority of possible
@ -138,25 +142,26 @@ enum Target {
Wasm,
// -------------------- Other language generation --------------------
/// Directly generate Lua code.
// Medium-priority codegen, since Lua is the only type of code that runs in e.g. plugins and
// scripts in some software, and it is difficult to generate efficient Lua without direct
// support from the compiler.
/// Directly generate Lua code.
Lua,
/// Directly generate Python code.
// Low-priority codegen; the same situation as Lua, but situations that require Python code with
// no alternatives are much less common than situations that require Lua code with no
// alternatives.
/// Directly generate Python code.
Python,
/// Directly generate Go code.
// Extremely low-priority codegen; almost no valid use cases.
/// Directly generate Go code.
Go,
/// Directly generate Ada code.
// Currently zero-priority codegen; no valid use cases whatsoever as far as I (Alex) can
// determine.
/// Directly generate Ada code.
Ada,
}
@ -166,6 +171,7 @@ impl Display for Target {
f,
"{}",
match self {
Target::UIR => "uir",
Target::CSource => "c",
Target::Assembly => "asm",
Target::ObjectFile => "obj",
@ -190,6 +196,7 @@ impl FromStr for Target {
use Target::*;
let targets = [
("uir", "Untyped intermediate representation.", UIR),
("c", "C source code.", CSource),
("asm", "Assembly code.", Assembly),
("obj", "Object file.", ObjectFile),

View File

@ -379,9 +379,7 @@ fn parse_expression<'a>(
.unwrap()
});
let unary = parse_unary(m, application);
let binary = (0..=10).rev().fold(unary.boxed(), |p, precedence| {
let binary = (0..=10).rev().fold(application.boxed(), |p, precedence| {
parse_binary(m, precedence, p).boxed()
});
@ -389,22 +387,6 @@ fn parse_expression<'a>(
})
}
fn parse_unary(
_m: &ParserMeta,
base: impl Parser<char, Expr, Error = Simple<char>> + Clone,
) -> impl Parser<char, Expr, Error = Simple<char>> + Clone {
pad(just("-").to("-"))
.repeated()
.then(base)
.map(|(ops, exp)| {
ops.into_iter().fold(exp, |exp, op| Expr::UnaryOp {
kind: op.to_string(),
val: Box::new(exp),
translation: "negate".to_string(),
})
})
}
fn parse_binary<'a>(
m: &'a ParserMeta,
prec: u32,

View File

@ -1,5 +1,7 @@
//! Syntax tree for Drim code.
use std::fmt::Display;
use num_bigint::BigUint;
/// A concrete syntax tree. This represents the full content of a Drim program, including all
@ -90,18 +92,6 @@ pub struct TypeConstructor {
/// The different kinds of expressions.
#[derive(Clone, Debug)]
pub enum Expr {
/// Unary operators, e.g., `-5`.
UnaryOp {
/// The text of the operator.
kind: String,
/// The value being operated upon.
val: Box<Expr>,
/// The function that the unary operator translates to.
translation: String,
},
/// Binary operators, e.g., `5 + 5`.
BinaryOp {
/// The text of the operator.
@ -258,6 +248,18 @@ pub struct Identifier {
pub elems: Vec<String>,
}
impl Display for Identifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (n, e) in self.elems.iter().enumerate() {
if n != 0 {
write!(f, "::")?;
}
write!(f, "{}", e)?;
}
Ok(())
}
}
/// Literal values included in source code.
#[derive(Clone, Debug)]
pub enum Literal {
@ -270,3 +272,13 @@ pub enum Literal {
/// `123.456`
Float(f64),
}
impl Display for Literal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Literal::String(s) => write!(f, "{}", s),
Literal::Integer(i) => write!(f, "{}", i),
Literal::Float(fl) => write!(f, "{}", fl),
}
}
}

View File

@ -1,3 +1,3 @@
// IR Test
def nul (a, b) = ();
def nul (a, b) = sin a;