added structs
This commit is contained in:
parent
0edbc46aa0
commit
47c6ccfa6b
97
Cargo.lock
generated
97
Cargo.lock
generated
|
@ -2,6 +2,103 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lang"
|
name = "lang"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"wat",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "leb128"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-encoder"
|
||||||
|
version = "0.219.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29cbbd772edcb8e7d524a82ee8cef8dd046fc14033796a754c3ad246d019fa54"
|
||||||
|
dependencies = [
|
||||||
|
"leb128",
|
||||||
|
"wasmparser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasmparser"
|
||||||
|
version = "0.219.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c771866898879073c53b565a6c7b49953795159836714ac56a5befb581227c5"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"indexmap",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wast"
|
||||||
|
version = "219.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4f79a9d9df79986a68689a6b40bcc8d5d40d807487b235bebc2ac69a242b54a1"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"leb128",
|
||||||
|
"memchr",
|
||||||
|
"unicode-width",
|
||||||
|
"wasm-encoder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wat"
|
||||||
|
version = "1.219.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8bc3cf014fb336883a411cd662f987abf6a1d2a27f2f0008616a0070bbf6bd0d"
|
||||||
|
dependencies = [
|
||||||
|
"wast",
|
||||||
|
]
|
||||||
|
|
|
@ -4,3 +4,4 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
wat = "1.219"
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
i := 0;
|
Point :: struct {x: integer};
|
||||||
|
a : integer : 10;
|
||||||
while i < 10 {
|
p :: Point{x: a};
|
||||||
i = i + 1;
|
|
||||||
print;
|
|
||||||
}
|
|
||||||
|
|
23
index.html
Normal file
23
index.html
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const importObject = {
|
||||||
|
console: {
|
||||||
|
log(arg) {
|
||||||
|
console.log(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
WebAssembly.instantiateStreaming(fetch("/test.wasm"), importObject)
|
||||||
|
.then(obj => {
|
||||||
|
obj.instance.exports.main();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
16
src/err.rs
16
src/err.rs
|
@ -3,7 +3,7 @@ use crate::Span;
|
||||||
pub type Result<T> = std::result::Result<T, Diagnostic>;
|
pub type Result<T> = std::result::Result<T, Diagnostic>;
|
||||||
|
|
||||||
pub fn error<T>() -> Result<T> {
|
pub fn error<T>() -> Result<T> {
|
||||||
Err(Diagnostic::new(""))
|
Err(Diagnostic::new("", None))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -14,10 +14,10 @@ pub struct Diagnostic {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Diagnostic {
|
impl Diagnostic {
|
||||||
pub fn new(reason: impl Into<String>) -> Self {
|
pub fn new(reason: impl Into<String>, span: Option<Span>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
reason: reason.into(),
|
reason: reason.into(),
|
||||||
span: None,
|
span,
|
||||||
backtrace: vec![],
|
backtrace: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,17 +58,19 @@ impl From<std::num::ParseIntError> for Diagnostic {
|
||||||
use std::num::IntErrorKind::*;
|
use std::num::IntErrorKind::*;
|
||||||
match value.kind() {
|
match value.kind() {
|
||||||
PosOverflow | NegOverflow => {
|
PosOverflow | NegOverflow => {
|
||||||
Diagnostic::new("Integer value is too large to represent")
|
Diagnostic::new("Integer value is too large to represent", None)
|
||||||
},
|
},
|
||||||
InvalidDigit => Diagnostic::new("Integer value containts invalid digits"),
|
InvalidDigit => {
|
||||||
_ => Diagnostic::new("Integer value could not be parsed"),
|
Diagnostic::new("Integer value containts invalid digits", None)
|
||||||
|
},
|
||||||
|
_ => Diagnostic::new("Integer value could not be parsed", None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std::num::ParseFloatError> for Diagnostic {
|
impl From<std::num::ParseFloatError> for Diagnostic {
|
||||||
fn from(_value: std::num::ParseFloatError) -> Self {
|
fn from(_value: std::num::ParseFloatError) -> Self {
|
||||||
Diagnostic::new("Float value could not be parsed")
|
Diagnostic::new("Float value could not be parsed", None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::{
|
use crate::{err::*, semantic::typecheck, Parser, Statement, Tokenizer};
|
||||||
err::*, treewalk::Interpreter, Parser, Statement, StatementKind, Token,
|
|
||||||
Tokenizer,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Module {
|
pub struct Module {
|
||||||
file_name: String,
|
file_name: String,
|
||||||
source: String,
|
source: String,
|
||||||
ast: Vec<Statement>,
|
pub program: Vec<Statement>,
|
||||||
errors: Vec<Diagnostic>,
|
errors: Vec<Diagnostic>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,19 +25,13 @@ impl Module {
|
||||||
|
|
||||||
pub fn from_string(file_name: String, source: String) -> Self {
|
pub fn from_string(file_name: String, source: String) -> Self {
|
||||||
let tokens = Tokenizer::new(source.chars()).filter(|t| t.0.is_meaningful());
|
let tokens = Tokenizer::new(source.chars()).filter(|t| t.0.is_meaningful());
|
||||||
let mut ast = vec![];
|
let statements = Parser::new(tokens);
|
||||||
|
let program = typecheck(statements.collect());
|
||||||
let mut errors = vec![];
|
let mut errors = vec![];
|
||||||
for statement in Parser::new(tokens) {
|
|
||||||
use StatementKind as s;
|
|
||||||
match statement.kind {
|
|
||||||
s::Error(e) => errors.push(e),
|
|
||||||
_ => ast.push(statement),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self {
|
Self {
|
||||||
file_name,
|
file_name,
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
ast,
|
program,
|
||||||
errors,
|
errors,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,16 +43,4 @@ impl Module {
|
||||||
pub fn ok(&self) -> bool {
|
pub fn ok(&self) -> bool {
|
||||||
self.errors.len() == 0
|
self.errors.len() == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute(&self) {
|
|
||||||
if !self.ok() {
|
|
||||||
for e in self.errors() {
|
|
||||||
eprintln!("{e}");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Interpreter::new(self.ast.clone().into_iter())
|
|
||||||
.run()
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@ mod err;
|
||||||
mod frontend;
|
mod frontend;
|
||||||
mod lookahead;
|
mod lookahead;
|
||||||
mod parse;
|
mod parse;
|
||||||
|
mod semantic;
|
||||||
mod token;
|
mod token;
|
||||||
mod treewalk;
|
mod treewalk;
|
||||||
mod types;
|
|
||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
|
|
||||||
use err::*;
|
use err::*;
|
||||||
|
@ -63,6 +63,8 @@ fn test_expression(expr: &str) {
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let module = frontend::Module::from_file("./demo.lang")?;
|
let module = frontend::Module::from_file("./demo.lang")?;
|
||||||
module.execute();
|
for s in &module.program {
|
||||||
|
println!("{s:?}");
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +1,53 @@
|
||||||
|
use crate::semantic::*;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Parameter {
|
pub struct Parameter {
|
||||||
name: String,
|
pub name: String,
|
||||||
type_: String,
|
pub type_str: String,
|
||||||
|
pub type_actual: Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for Parameter {
|
impl std::fmt::Debug for Parameter {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}: {}", self.name, self.type_)
|
write!(f, "{}: {:?}", self.name, self.type_actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ExpressionKind {
|
pub enum Immediate {
|
||||||
Integer(i64),
|
Integer(i64),
|
||||||
Real(f64),
|
Real(f64),
|
||||||
String(String),
|
String(String),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum ExpressionKind {
|
||||||
|
Immediate(Immediate),
|
||||||
Identifier(String),
|
Identifier(String),
|
||||||
Binary {
|
Binary {
|
||||||
token: TokenKind,
|
op: BinaryOp,
|
||||||
left: Box<Expression>,
|
left: Box<Expression>,
|
||||||
right: Box<Expression>,
|
right: Box<Expression>,
|
||||||
},
|
},
|
||||||
Unary {
|
Unary {
|
||||||
token: TokenKind,
|
op: UnaryOp,
|
||||||
child: Box<Expression>,
|
child: Box<Expression>,
|
||||||
},
|
},
|
||||||
Parenthesis(Box<Expression>),
|
Parenthesis(Box<Expression>),
|
||||||
Function {
|
Function {
|
||||||
params: Vec<Parameter>,
|
params: Vec<Parameter>,
|
||||||
returns: Option<String>,
|
returns_str: Option<String>,
|
||||||
|
returns_actual: Type,
|
||||||
body: Vec<Statement>,
|
body: Vec<Statement>,
|
||||||
},
|
},
|
||||||
|
Struct(Vec<Parameter>),
|
||||||
|
StructLiteral {
|
||||||
|
name: String,
|
||||||
|
args: Vec<(String, Expression)>,
|
||||||
|
},
|
||||||
Call {
|
Call {
|
||||||
callee: Box<Expression>,
|
callee: Box<Expression>,
|
||||||
args: Vec<Expression>,
|
args: Vec<Expression>,
|
||||||
|
@ -48,39 +62,55 @@ pub enum ExpressionKind {
|
||||||
pub struct Expression {
|
pub struct Expression {
|
||||||
pub kind: ExpressionKind,
|
pub kind: ExpressionKind,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
|
pub type_: Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expression {
|
impl Expression {
|
||||||
pub fn new(kind: ExpressionKind, span: Span) -> Self {
|
pub fn new(kind: ExpressionKind, span: Span) -> Self {
|
||||||
Self { kind, span }
|
Self {
|
||||||
|
kind,
|
||||||
|
span,
|
||||||
|
type_: Type::Ambiguous,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for ExpressionKind {
|
impl std::fmt::Debug for ExpressionKind {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
use ExpressionKind as e;
|
use ExpressionKind as e;
|
||||||
|
use Immediate as im;
|
||||||
match self {
|
match self {
|
||||||
e::Integer(i) => write!(f, "{i}"),
|
e::Immediate(i) => match i {
|
||||||
e::Binary { token, left, right } => {
|
im::Integer(i) => write!(f, "{i}"),
|
||||||
|
im::Real(fp) => write!(f, "{fp}"),
|
||||||
|
im::String(s) => write!(f, r#""{s}""#),
|
||||||
|
im::Boolean(b) => write!(f, "{b}"),
|
||||||
|
},
|
||||||
|
e::Binary {
|
||||||
|
op: token,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
} => {
|
||||||
write!(f, "({left:?} {token:?} {right:?})")
|
write!(f, "({left:?} {token:?} {right:?})")
|
||||||
},
|
},
|
||||||
e::Parenthesis(inner) => write!(f, "{inner:?}"),
|
e::Parenthesis(inner) => write!(f, "{inner:?}"),
|
||||||
e::Unary { token, child } => {
|
e::Unary { op: token, child } => {
|
||||||
write!(f, "({token:?} {child:?})")
|
write!(f, "({token:?} {child:?})")
|
||||||
},
|
},
|
||||||
e::Real(fp) => write!(f, "{fp}"),
|
|
||||||
e::String(s) => write!(f, r#""{s}""#),
|
|
||||||
e::Identifier(i) => write!(f, "{i}"),
|
e::Identifier(i) => write!(f, "{i}"),
|
||||||
e::Boolean(b) => write!(f, "{b}"),
|
|
||||||
e::Call { callee, args } => write!(f, "({callee:?} call {args:?})"),
|
e::Call { callee, args } => write!(f, "({callee:?} call {args:?})"),
|
||||||
e::Field { namespace, field } => {
|
e::Field { namespace, field } => {
|
||||||
write!(f, "({namespace:?} . {field:?})")
|
write!(f, "({namespace:?} . {field:?})")
|
||||||
},
|
},
|
||||||
e::Function {
|
e::Function {
|
||||||
params, returns, ..
|
params,
|
||||||
|
returns_actual,
|
||||||
|
..
|
||||||
} => {
|
} => {
|
||||||
write!(f, "(fn({params:?}) -> {returns:?})")
|
write!(f, "(fn({params:?}) -> {returns_actual:?})")
|
||||||
},
|
},
|
||||||
|
e::Struct(params) => write!(f, "struct {{ {params:?} }}"),
|
||||||
|
e::StructLiteral { name, args } => write!(f, "{name} {{ {args:?} }}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,16 +127,22 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
use TokenKind as t;
|
use TokenKind as t;
|
||||||
let next = self.peek(0)?;
|
let next = self.peek(0)?;
|
||||||
// Unary prefix expression
|
// Unary prefix expression
|
||||||
let mut current = if let Ok(p) = unary_prefix_prec(&next.0) {
|
let mut current = if let Ok(operator) = UnaryOp::try_from(&next.0) {
|
||||||
let operator = self.next_tok().expect("unreachable");
|
self.skip(1);
|
||||||
|
let span = next.1;
|
||||||
|
if operator.assoc() == RIGHT_ASSOC {
|
||||||
|
return error()
|
||||||
|
.reason(format!("The {} operator must come after a value", operator))
|
||||||
|
.span(&span);
|
||||||
|
}
|
||||||
let child = self
|
let child = self
|
||||||
.expression(p)
|
.expression(operator.precedence())
|
||||||
.trace(format!("while parsing unary {}", operator.0))
|
.trace(format!("while parsing unary {}", operator))
|
||||||
.span(&operator.1)?;
|
.span(&span)?;
|
||||||
let span = child.span + operator.1;
|
let span = span + child.span;
|
||||||
Expression::new(
|
Expression::new(
|
||||||
e::Unary {
|
e::Unary {
|
||||||
token: operator.0,
|
op: operator,
|
||||||
child: child.into(),
|
child: child.into(),
|
||||||
},
|
},
|
||||||
span,
|
span,
|
||||||
|
@ -120,21 +156,23 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
// Precedence climbing loop
|
// Precedence climbing loop
|
||||||
while let Ok(next) = self.peek(0) {
|
while let Ok(next) = self.peek(0) {
|
||||||
// Binary infix
|
// Binary infix
|
||||||
if let Ok((new_precedence, left_assoc)) = binary_prec(&next.0) {
|
if let Ok(operator) = BinaryOp::try_from(&next.0) {
|
||||||
if (!left_assoc && new_precedence <= precedence)
|
let new_precedence = operator.precedence();
|
||||||
|
if ((operator.assoc() == LEFT_ASSOC) && new_precedence <= precedence)
|
||||||
|| (new_precedence < precedence)
|
|| (new_precedence < precedence)
|
||||||
{
|
{
|
||||||
return Ok(current);
|
return Ok(current);
|
||||||
}
|
}
|
||||||
let operator = self.next_tok().expect("unreachable");
|
self.skip(1);
|
||||||
|
let span = next.1;
|
||||||
let rhs = self
|
let rhs = self
|
||||||
.expression(new_precedence)
|
.expression(new_precedence)
|
||||||
.trace(format!("while parsing binary {}", operator.0))
|
.trace(format!("while parsing binary {}", operator))
|
||||||
.span(&operator.1)?;
|
.span(&span)?;
|
||||||
let span = next.1 + rhs.span;
|
let span = next.1 + rhs.span;
|
||||||
current = Expression::new(
|
current = Expression::new(
|
||||||
e::Binary {
|
e::Binary {
|
||||||
token: operator.0,
|
op: operator,
|
||||||
left: current.into(),
|
left: current.into(),
|
||||||
right: rhs.into(),
|
right: rhs.into(),
|
||||||
},
|
},
|
||||||
|
@ -187,12 +225,20 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Unary postfix
|
// Unary postfix
|
||||||
else if let Ok(_) = unary_postfix_prec(&next.0) {
|
else if let Ok(operator) = UnaryOp::try_from(&next.0) {
|
||||||
let operator = self.next_tok().expect("unreachable");
|
self.skip(1);
|
||||||
let span = next.1 + operator.1;
|
let span = next.1;
|
||||||
|
if operator.assoc() == RIGHT_ASSOC {
|
||||||
|
return error()
|
||||||
|
.reason(format!(
|
||||||
|
"The {} operator must come before a value",
|
||||||
|
operator
|
||||||
|
))
|
||||||
|
.span(&span);
|
||||||
|
}
|
||||||
current = Expression::new(
|
current = Expression::new(
|
||||||
e::Unary {
|
e::Unary {
|
||||||
token: operator.0,
|
op: operator,
|
||||||
child: current.into(),
|
child: current.into(),
|
||||||
},
|
},
|
||||||
span,
|
span,
|
||||||
|
@ -206,16 +252,32 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
|
|
||||||
fn primary(&mut self) -> Result<Expression> {
|
fn primary(&mut self) -> Result<Expression> {
|
||||||
use ExpressionKind as e;
|
use ExpressionKind as e;
|
||||||
|
use Immediate as im;
|
||||||
use TokenKind as t;
|
use TokenKind as t;
|
||||||
let next = self.peek(0)?;
|
let next = self.peek(0)?;
|
||||||
let mut span = next.1;
|
let mut span = next.1;
|
||||||
let kind = match next.0 {
|
let kind = match next.0 {
|
||||||
t::IntegerLiteral(i) => e::Integer(i),
|
t::IntegerLiteral(i) => {
|
||||||
t::FloatLiteral(f) => e::Real(f),
|
self.skip(1);
|
||||||
t::StringLiteral(s) => e::String(s),
|
e::Immediate(im::Integer(i))
|
||||||
t::True => e::Boolean(true),
|
},
|
||||||
t::Identifier(i) => e::Identifier(i),
|
t::FloatLiteral(f) => {
|
||||||
// function
|
self.skip(1);
|
||||||
|
e::Immediate(im::Real(f))
|
||||||
|
},
|
||||||
|
t::StringLiteral(s) => {
|
||||||
|
self.skip(1);
|
||||||
|
e::Immediate(im::String(s))
|
||||||
|
},
|
||||||
|
t::True => {
|
||||||
|
self.skip(1);
|
||||||
|
e::Immediate(im::Boolean(true))
|
||||||
|
},
|
||||||
|
t::False => {
|
||||||
|
self.skip(1);
|
||||||
|
e::Immediate(im::Boolean(true))
|
||||||
|
},
|
||||||
|
// Function definition
|
||||||
t::LeftParen
|
t::LeftParen
|
||||||
if (self.look(1, t::Identifier("".into())).is_ok()
|
if (self.look(1, t::Identifier("".into())).is_ok()
|
||||||
&& self.look(2, t::Colon).is_ok())
|
&& self.look(2, t::Colon).is_ok())
|
||||||
|
@ -236,7 +298,11 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
.identifier()
|
.identifier()
|
||||||
.trace_span(span, "while parsing function parameter type")?;
|
.trace_span(span, "while parsing function parameter type")?;
|
||||||
span = span + span2;
|
span = span + span2;
|
||||||
params.push(Parameter { name, type_ });
|
params.push(Parameter {
|
||||||
|
name,
|
||||||
|
type_str: type_,
|
||||||
|
type_actual: Type::Ambiguous,
|
||||||
|
});
|
||||||
if !self.eat(t::Comma).is_ok() {
|
if !self.eat(t::Comma).is_ok() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -245,7 +311,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
.eat(t::RightParen)
|
.eat(t::RightParen)
|
||||||
.span(&span)
|
.span(&span)
|
||||||
.trace_span(span, "while parsing function definition")?;
|
.trace_span(span, "while parsing function definition")?;
|
||||||
let returns = if let Ok(Token(_, span2)) = self.eat(t::Arrow) {
|
let returns_str = if let Ok(Token(_, span2)) = self.eat(t::Arrow) {
|
||||||
span = span + span2;
|
span = span + span2;
|
||||||
let (identifier, span2) = self
|
let (identifier, span2) = self
|
||||||
.identifier()
|
.identifier()
|
||||||
|
@ -262,18 +328,78 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
span = span + span2;
|
span = span + span2;
|
||||||
e::Function {
|
e::Function {
|
||||||
params,
|
params,
|
||||||
returns,
|
returns_str,
|
||||||
|
returns_actual: Type::Ambiguous,
|
||||||
body,
|
body,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// parenthetical
|
// Struct definition
|
||||||
|
t::Struct => {
|
||||||
|
self.skip(1);
|
||||||
|
self.eat(t::LeftBrace)?;
|
||||||
|
let mut params = vec![];
|
||||||
|
loop {
|
||||||
|
let (name, span2) = match self.identifier() {
|
||||||
|
Ok((name, span)) => (name, span),
|
||||||
|
Err(_) => break,
|
||||||
|
};
|
||||||
|
span = span + span2;
|
||||||
|
self
|
||||||
|
.eat(t::Colon)
|
||||||
|
.trace_span(span, "while parsing struct parameter type")?;
|
||||||
|
let (type_, span2) = self
|
||||||
|
.identifier()
|
||||||
|
.trace_span(span, "while parsing struct parameter type")?;
|
||||||
|
span = span + span2;
|
||||||
|
params.push(Parameter {
|
||||||
|
name,
|
||||||
|
type_str: type_,
|
||||||
|
type_actual: Type::Ambiguous,
|
||||||
|
});
|
||||||
|
if !self.eat(t::Comma).is_ok() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.eat(t::RightBrace)?;
|
||||||
|
e::Struct(params)
|
||||||
|
},
|
||||||
|
// Struct literal
|
||||||
|
t::Identifier(name) if self.look(1, t::LeftBrace).is_ok() => {
|
||||||
|
self.skip(2);
|
||||||
|
let mut args = vec![];
|
||||||
|
loop {
|
||||||
|
let (name, span2) = match self.identifier() {
|
||||||
|
Ok((name, span)) => (name, span),
|
||||||
|
Err(_) => break,
|
||||||
|
};
|
||||||
|
span = span + span2;
|
||||||
|
self
|
||||||
|
.eat(t::Colon)
|
||||||
|
.trace_span(span, "while parsing struct parameter type")?;
|
||||||
|
let expr = self
|
||||||
|
.expression(0)
|
||||||
|
.trace_span(span, "while parsing struct parameter type")?;
|
||||||
|
span = span + expr.span;
|
||||||
|
args.push((name, expr));
|
||||||
|
if !self.eat(t::Comma).is_ok() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.eat(t::RightBrace)?;
|
||||||
|
e::StructLiteral { name, args }
|
||||||
|
},
|
||||||
|
t::Identifier(i) => {
|
||||||
|
self.skip(1);
|
||||||
|
e::Identifier(i)
|
||||||
|
},
|
||||||
|
// Parenthetical
|
||||||
t::LeftParen => {
|
t::LeftParen => {
|
||||||
self.skip(1);
|
self.skip(1);
|
||||||
let expr = self
|
let expr = self
|
||||||
.expression(0)
|
.expression(0)
|
||||||
.trace("while parsing parenthesized expression")?;
|
.trace("while parsing parenthesized expression")?;
|
||||||
self
|
self
|
||||||
.look(0, t::RightParen)
|
.eat(t::RightParen)
|
||||||
.reason("Unclosed '('")
|
.reason("Unclosed '('")
|
||||||
.span(&expr.span)?;
|
.span(&expr.span)?;
|
||||||
e::Parenthesis(expr.into())
|
e::Parenthesis(expr.into())
|
||||||
|
@ -284,8 +410,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
.reason(format!("Expected expression, found {}", next.0));
|
.reason(format!("Expected expression, found {}", next.0));
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
self.skip(1);
|
Ok(Expression::new(kind, span))
|
||||||
Ok(Expression { kind, span })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn identifier(&mut self) -> Result<(String, Span)> {
|
fn identifier(&mut self) -> Result<(String, Span)> {
|
||||||
|
|
118
src/parse/mod.rs
118
src/parse/mod.rs
|
@ -8,51 +8,82 @@ use crate::{Span, Token, TokenKind};
|
||||||
|
|
||||||
pub type Precedence = usize;
|
pub type Precedence = usize;
|
||||||
|
|
||||||
|
macro_rules! op {
|
||||||
|
($name:ident; $($op:ident, $prec:expr, $assoc:expr);*;) => {
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum $name {
|
||||||
|
$($op,)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $name {
|
||||||
|
pub fn precedence(&self) -> Precedence {
|
||||||
|
match self {
|
||||||
|
$(Self::$op => $prec),*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assoc(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
$(Self::$op => $assoc),*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for $name {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&TokenKind> for $name {
|
||||||
|
type Error = Diagnostic;
|
||||||
|
fn try_from(value: &TokenKind) -> Result<Self> {
|
||||||
|
match value {
|
||||||
|
$(TokenKind::$op => Ok(Self::$op),)*
|
||||||
|
_ => error().reason(format!("Invalid operator {value}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const RIGHT_ASSOC: bool = true;
|
||||||
|
const LEFT_ASSOC: bool = false;
|
||||||
|
|
||||||
|
// Name, precedence, associativity;
|
||||||
|
op! {
|
||||||
|
BinaryOp;
|
||||||
|
Star, 10, LEFT_ASSOC;
|
||||||
|
Slash, 10, LEFT_ASSOC;
|
||||||
|
Percent, 10, LEFT_ASSOC;
|
||||||
|
Plus, 9, LEFT_ASSOC;
|
||||||
|
Minus, 9, LEFT_ASSOC;
|
||||||
|
And, 8, LEFT_ASSOC;
|
||||||
|
Nand, 8, LEFT_ASSOC;
|
||||||
|
Xor, 7, LEFT_ASSOC;
|
||||||
|
Xnor, 7, LEFT_ASSOC;
|
||||||
|
Or, 6, LEFT_ASSOC;
|
||||||
|
Nor, 6, LEFT_ASSOC;
|
||||||
|
DoubleEqual, 5, LEFT_ASSOC;
|
||||||
|
BangEqual, 5, LEFT_ASSOC;
|
||||||
|
Less, 5, LEFT_ASSOC;
|
||||||
|
LessEqual, 5, LEFT_ASSOC;
|
||||||
|
Greater, 5, LEFT_ASSOC;
|
||||||
|
GreaterEqual, 5, LEFT_ASSOC;
|
||||||
|
}
|
||||||
|
|
||||||
|
op! {
|
||||||
|
UnaryOp;
|
||||||
|
Bang, 12, RIGHT_ASSOC;
|
||||||
|
Question, 12, RIGHT_ASSOC;
|
||||||
|
Minus, 11, LEFT_ASSOC;
|
||||||
|
Plus, 11, LEFT_ASSOC;
|
||||||
|
Not, 11, LEFT_ASSOC;
|
||||||
|
}
|
||||||
|
|
||||||
const FIELD_PREC: Precedence = 13;
|
const FIELD_PREC: Precedence = 13;
|
||||||
const CALL_PREC: Precedence = 12;
|
const CALL_PREC: Precedence = 12;
|
||||||
|
|
||||||
fn binary_prec(tok: &TokenKind) -> Result<(Precedence, bool)> {
|
|
||||||
use TokenKind::*;
|
|
||||||
Ok(match tok {
|
|
||||||
Star | Slash | Percent => (10, false),
|
|
||||||
Plus | Minus => (9, false),
|
|
||||||
And | Nand => (8, false),
|
|
||||||
Xor | Xnor => (7, false),
|
|
||||||
Or | Nor => (6, false),
|
|
||||||
DoubleEqual | BangEqual | Less | LessEqual | Greater | GreaterEqual => {
|
|
||||||
(5, false)
|
|
||||||
},
|
|
||||||
//Colon => Some((5, false)),
|
|
||||||
_ => {
|
|
||||||
return error().reason(format!("{} is not a valid binary operator", tok));
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unary_prefix_prec(tok: &TokenKind) -> Result<Precedence> {
|
|
||||||
use TokenKind::*;
|
|
||||||
Ok(match tok {
|
|
||||||
Minus | Not => 11,
|
|
||||||
Break => 3,
|
|
||||||
_ => {
|
|
||||||
return error()
|
|
||||||
.reason(format!("{tok} is not a valid prefix unary operator"));
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unary_postfix_prec(tok: &TokenKind) -> Result<Precedence> {
|
|
||||||
use TokenKind::*;
|
|
||||||
Ok(match tok {
|
|
||||||
Question => 12,
|
|
||||||
Bang => 12,
|
|
||||||
_ => {
|
|
||||||
return error()
|
|
||||||
.reason(format!("{tok} is not a valid postfix unary operator"));
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const PARSER_LOOKAHEAD: usize = 3;
|
const PARSER_LOOKAHEAD: usize = 3;
|
||||||
|
|
||||||
type TokenIter<I> = crate::Window<PARSER_LOOKAHEAD, Token, I>;
|
type TokenIter<I> = crate::Window<PARSER_LOOKAHEAD, Token, I>;
|
||||||
|
@ -146,8 +177,7 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
let mut span = self.eat(t::LeftBrace).reason("Expected block")?.1;
|
let mut span = self.eat(t::LeftBrace).reason("Expected block")?.1;
|
||||||
let mut statements = vec![];
|
let mut statements = vec![];
|
||||||
loop {
|
loop {
|
||||||
let next = self.peek(0)?;
|
span = span + self.peek(0)?.1;
|
||||||
span = span + next.1;
|
|
||||||
match self.eat(t::RightBrace) {
|
match self.eat(t::RightBrace) {
|
||||||
Ok(t) => {
|
Ok(t) => {
|
||||||
span = span + t.1;
|
span = span + t.1;
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
|
use crate::semantic::Type;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum StatementKind {
|
pub enum StatementKind {
|
||||||
Mutable {
|
Declaration {
|
||||||
name: String,
|
name: String,
|
||||||
type_: Option<String>,
|
type_str: Option<String>,
|
||||||
value: Option<Expression>,
|
type_actual: Type,
|
||||||
},
|
|
||||||
Immutable {
|
|
||||||
name: String,
|
|
||||||
type_: Option<String>,
|
|
||||||
value: Expression,
|
value: Expression,
|
||||||
|
mutable: bool,
|
||||||
},
|
},
|
||||||
Assignment {
|
Assignment {
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -49,44 +48,47 @@ impl<I: Iterator<Item = Token>> Parser<I> {
|
||||||
(Token(t::Identifier(name), span2), Ok(Token(t::Colon, span3))) => {
|
(Token(t::Identifier(name), span2), Ok(Token(t::Colon, span3))) => {
|
||||||
self.skip(2);
|
self.skip(2);
|
||||||
span = span + span2 + span3;
|
span = span + span2 + span3;
|
||||||
// type
|
let type_str = match self.eat(t::Identifier("".into())) {
|
||||||
let type_ = match self.eat(t::Identifier("".into())) {
|
|
||||||
Ok(Token(t::Identifier(s), span2)) => {
|
Ok(Token(t::Identifier(s), span2)) => {
|
||||||
span = span + span2;
|
span = span + span2;
|
||||||
Some(s)
|
Some(s)
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
// value
|
let mutable = if self.eat(t::Equal).is_ok() {
|
||||||
match self.eat(t::Equal).or_else(|_| self.eat(t::Colon)) {
|
true
|
||||||
Ok(Token(t::Colon, span2)) => {
|
} else if self.eat(t::Colon).is_ok() {
|
||||||
span = span + span2;
|
false
|
||||||
let value = self
|
} else {
|
||||||
.expression(0)
|
return error()
|
||||||
.trace_span(span, "while parsing mutable declaration")?;
|
.reason(format!("Declaration of '{}' must be initialized", name))
|
||||||
span = span + value.span;
|
.span(&span);
|
||||||
Statement {
|
};
|
||||||
kind: s::Immutable { name, type_, value },
|
let value = self
|
||||||
span,
|
.expression(0)
|
||||||
}
|
.trace_span(span, "while parsing declaration")?;
|
||||||
|
span = span + value.span;
|
||||||
|
let no_semicolon = if let ExpressionKind::Function { .. } = value.kind {
|
||||||
|
true
|
||||||
|
} else if let ExpressionKind::Struct(_) = value.kind {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
let s = Statement {
|
||||||
|
kind: s::Declaration {
|
||||||
|
name,
|
||||||
|
type_str,
|
||||||
|
type_actual: Type::Ambiguous,
|
||||||
|
value,
|
||||||
|
mutable,
|
||||||
},
|
},
|
||||||
Ok(Token(t::Equal, span2)) => {
|
span,
|
||||||
span = span + span2;
|
};
|
||||||
let value = self
|
if no_semicolon {
|
||||||
.expression(0)
|
return Ok(s);
|
||||||
.trace_span(span, "while parsing mutable declaration")?;
|
|
||||||
span = span + value.span;
|
|
||||||
Statement {
|
|
||||||
kind: s::Mutable {
|
|
||||||
name,
|
|
||||||
type_,
|
|
||||||
value: Some(value),
|
|
||||||
},
|
|
||||||
span,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => return error().reason("Expected expression").span(&span),
|
|
||||||
}
|
}
|
||||||
|
s
|
||||||
},
|
},
|
||||||
// Assignment
|
// Assignment
|
||||||
(Token(t::Identifier(name), span2), Ok(Token(t::Equal, span3))) => {
|
(Token(t::Identifier(name), span2), Ok(Token(t::Equal, span3))) => {
|
||||||
|
|
5
src/semantic/mod.rs
Normal file
5
src/semantic/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
mod primitives;
|
||||||
|
mod types;
|
||||||
|
|
||||||
|
pub use primitives::*;
|
||||||
|
pub use types::*;
|
172
src/semantic/primitives.rs
Normal file
172
src/semantic/primitives.rs
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
use crate::{BinaryOp, UnaryOp};
|
||||||
|
|
||||||
|
use crate::err::*;
|
||||||
|
|
||||||
|
macro_rules! primitives {
|
||||||
|
( $($i:ident),* ) => {
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[allow(non_camel_case_types, dead_code)]
|
||||||
|
pub enum Primitive {
|
||||||
|
integer_ambiguous,
|
||||||
|
real_ambiguous,
|
||||||
|
$($i,)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Primitive {
|
||||||
|
pub fn from_string(string: &str) -> Option<Self> {
|
||||||
|
match string {
|
||||||
|
$(stringify!{$i} => Some(Self::$i),)*
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for Primitive {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Primitive::integer_ambiguous => write!(f, "<ambiguous integer>"),
|
||||||
|
Primitive::real_ambiguous => write!(f, "<ambiguous real>"),
|
||||||
|
$(Primitive::$i => write!(f, stringify!{$i}),)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
primitives! {
|
||||||
|
w8, w16, w32, w64, whole,
|
||||||
|
i8, i16, i32, i64, integer,
|
||||||
|
r32, r64, real,
|
||||||
|
boolean,
|
||||||
|
string, glyph
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! selfsame_basic {
|
||||||
|
( $lhs:ident, $op:ident, $rhs:ident, $binop:ident, $i:ident ) => {
|
||||||
|
if ($i == $rhs && $rhs == $lhs) && $op == BinaryOp::$binop {
|
||||||
|
return Ok($i);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
( $lhs:ident, $op:ident, $rhs:ident; $($i:ident),* ) => {
|
||||||
|
$(
|
||||||
|
selfsame_basic!($lhs, $op, $rhs, Plus, $i);
|
||||||
|
selfsame_basic!($lhs, $op, $rhs, Minus, $i);
|
||||||
|
selfsame_basic!($lhs, $op, $rhs, Star, $i);
|
||||||
|
selfsame_basic!($lhs, $op, $rhs, Slash, $i);
|
||||||
|
)*
|
||||||
|
logical!($lhs, $op, $rhs; $($i),*);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! logical {
|
||||||
|
( $lhs:ident, $op:ident, $rhs:ident; $($i:ident),* ) => {
|
||||||
|
$(
|
||||||
|
selfsame_basic!($lhs, $op, $rhs, And, $i);
|
||||||
|
selfsame_basic!($lhs, $op, $rhs, Nand, $i);
|
||||||
|
selfsame_basic!($lhs, $op, $rhs, Xor, $i);
|
||||||
|
selfsame_basic!($lhs, $op, $rhs, Xnor, $i);
|
||||||
|
selfsame_basic!($lhs, $op, $rhs, Or, $i);
|
||||||
|
selfsame_basic!($lhs, $op, $rhs, Nor, $i);
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! comparison {
|
||||||
|
( $lhs:ident, $op:ident, $rhs:ident, $binop:ident, $i:ident ) => {
|
||||||
|
if ($i == $rhs && $rhs == $lhs) && $op == BinaryOp::$binop {
|
||||||
|
return Ok(boolean);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
( $lhs:ident, $op:ident, $rhs:ident; $($i:ident),* ) => {
|
||||||
|
$(
|
||||||
|
comparison!($lhs, $op, $rhs, DoubleEqual, $i);
|
||||||
|
comparison!($lhs, $op, $rhs, BangEqual, $i);
|
||||||
|
comparison!($lhs, $op, $rhs, Less, $i);
|
||||||
|
comparison!($lhs, $op, $rhs, LessEqual, $i);
|
||||||
|
comparison!($lhs, $op, $rhs, Greater, $i);
|
||||||
|
comparison!($lhs, $op, $rhs, GreaterEqual, $i);
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Primitive {
|
||||||
|
pub fn coerce_ambiguous(
|
||||||
|
lhs: Primitive,
|
||||||
|
rhs: Primitive,
|
||||||
|
) -> (Primitive, Primitive) {
|
||||||
|
use Primitive::*;
|
||||||
|
let is_int = |i| {
|
||||||
|
if let i8 | i16 | i32 | i64 | integer | integer_ambiguous = i {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let is_real = |r| {
|
||||||
|
if let r32 | r64 | real | real_ambiguous = r {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Ambiguous integer coercion
|
||||||
|
if lhs == integer_ambiguous && is_int(rhs) {
|
||||||
|
return (rhs, rhs);
|
||||||
|
} else if rhs == integer_ambiguous && is_int(lhs) {
|
||||||
|
return (lhs, lhs);
|
||||||
|
}
|
||||||
|
// Ambiguous real coercion
|
||||||
|
if lhs == real_ambiguous && is_real(rhs) {
|
||||||
|
return (rhs, rhs);
|
||||||
|
} else if rhs == real_ambiguous && is_real(lhs) {
|
||||||
|
return (lhs, lhs);
|
||||||
|
}
|
||||||
|
return (lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn binary_op(
|
||||||
|
lhs: Primitive,
|
||||||
|
op: BinaryOp,
|
||||||
|
rhs: Primitive,
|
||||||
|
) -> Result<Primitive> {
|
||||||
|
use Primitive::*;
|
||||||
|
let (lhs, rhs) = Primitive::coerce_ambiguous(lhs, rhs);
|
||||||
|
selfsame_basic! {
|
||||||
|
lhs, op, rhs; w8, w16, w32, w64, i8, i16, i32, i64,
|
||||||
|
integer, integer_ambiguous, real, real_ambiguous
|
||||||
|
}
|
||||||
|
logical! { lhs, op, rhs; boolean }
|
||||||
|
comparison! {
|
||||||
|
lhs, op, rhs; w8, w16, w32, w64, i8, i16, i32, i64,
|
||||||
|
integer, integer_ambiguous, real, real_ambiguous,
|
||||||
|
boolean, string
|
||||||
|
}
|
||||||
|
error().reason(format!(
|
||||||
|
"Binary {} is not defined for {} and {}",
|
||||||
|
op, lhs, rhs
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unary_op(op: UnaryOp, child: Primitive) -> Result<Primitive> {
|
||||||
|
use Primitive::*;
|
||||||
|
use UnaryOp::*;
|
||||||
|
let e =
|
||||||
|
error().reason(format!("Unary {} is not defined for {}", op, child));
|
||||||
|
match op {
|
||||||
|
Minus => match child {
|
||||||
|
boolean | string | glyph | w8 | w16 | w32 | w64 => e,
|
||||||
|
_ => Ok(child),
|
||||||
|
},
|
||||||
|
Plus => match child {
|
||||||
|
boolean | string | glyph => e,
|
||||||
|
_ => Ok(child),
|
||||||
|
},
|
||||||
|
Not => match child {
|
||||||
|
string | glyph => e,
|
||||||
|
_ => Ok(child),
|
||||||
|
},
|
||||||
|
_ => error().reason(format!("Unary {} is not implemented (yet)", op)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
522
src/semantic/types.rs
Normal file
522
src/semantic/types.rs
Normal file
|
@ -0,0 +1,522 @@
|
||||||
|
use crate::{
|
||||||
|
BinaryOp, Expression, ExpressionKind, Immediate, Parameter, Statement,
|
||||||
|
StatementKind, UnaryOp,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::primitives::*;
|
||||||
|
use crate::err::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Type {
|
||||||
|
Ambiguous,
|
||||||
|
Nothing,
|
||||||
|
Prim(Primitive),
|
||||||
|
Struct(Vec<Parameter>),
|
||||||
|
Function {
|
||||||
|
params: Vec<Type>,
|
||||||
|
returns: Box<Type>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Type {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
use Type::*;
|
||||||
|
match (self, other) {
|
||||||
|
(Ambiguous, Ambiguous) => true,
|
||||||
|
(Prim(p1), Prim(p2)) if p1 == p2 => true,
|
||||||
|
(Struct(p1), Struct(p2)) => p1
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.type_actual.clone())
|
||||||
|
.eq(p2.iter().map(|p| p.type_actual.clone())),
|
||||||
|
(
|
||||||
|
Function {
|
||||||
|
params: p1,
|
||||||
|
returns: r1,
|
||||||
|
},
|
||||||
|
Function {
|
||||||
|
params: p2,
|
||||||
|
returns: r2,
|
||||||
|
},
|
||||||
|
) => p1.iter().eq(p2.iter()) && r1 == r2,
|
||||||
|
(Nothing, Nothing) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Type {
|
||||||
|
pub fn from_string(value: &str) -> Self {
|
||||||
|
if let Some(p) = Primitive::from_string(value) {
|
||||||
|
Type::Prim(p)
|
||||||
|
} else {
|
||||||
|
Type::Ambiguous
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn binary_op(lhs: &Type, op: BinaryOp, rhs: &Type) -> Result<Type> {
|
||||||
|
use Type as t;
|
||||||
|
let e = error().reason(format!(
|
||||||
|
"Binary {op} is not defined for {lhs:?} and {rhs:?}",
|
||||||
|
));
|
||||||
|
match (lhs, rhs) {
|
||||||
|
(t::Prim(a), t::Prim(b)) => {
|
||||||
|
let p = Primitive::binary_op(*a, op, *b)?;
|
||||||
|
Ok(t::Prim(p))
|
||||||
|
},
|
||||||
|
_ => e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unary_op(op: UnaryOp, child: &Type) -> Result<Type> {
|
||||||
|
use Type as t;
|
||||||
|
if let t::Prim(p) = child {
|
||||||
|
let p = Primitive::unary_op(op, *p)?;
|
||||||
|
Ok(t::Prim(p))
|
||||||
|
} else {
|
||||||
|
error().reason(format!("Unary {op} is not defined for {child:?}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn coerce(expect: &Type, actual: &Type) -> Option<Type> {
|
||||||
|
use Primitive as p;
|
||||||
|
use Type::*;
|
||||||
|
match (expect, actual) {
|
||||||
|
(Ambiguous, Ambiguous) => None,
|
||||||
|
(Ambiguous, Prim(p::integer_ambiguous)) => Some(Prim(p::integer)),
|
||||||
|
(Ambiguous, Prim(p::real_ambiguous)) => Some(Prim(p::real)),
|
||||||
|
(Ambiguous, t) => Some(t.clone()),
|
||||||
|
(Prim(p1), Prim(p2)) => {
|
||||||
|
let (p1, p2) = Primitive::coerce_ambiguous(*p1, *p2);
|
||||||
|
if p1 != p2 { None } else { Some(Type::Prim(p1)) }
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
enum Symbol {
|
||||||
|
Var(String, Type, bool),
|
||||||
|
Type(String, Type),
|
||||||
|
BlockStart,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct SymbolTable {
|
||||||
|
syms: Vec<Symbol>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SymbolTable {
|
||||||
|
fn define_var(&mut self, name: String, type_: Type, mutable: bool) {
|
||||||
|
self.syms.push(Symbol::Var(name, type_, mutable));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn define_type(&mut self, name: String, type_: Type) {
|
||||||
|
self.syms.push(Symbol::Type(name, type_));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_block(&mut self) {
|
||||||
|
self.syms.push(Symbol::BlockStart);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end_block(&mut self) {
|
||||||
|
while !self.syms.is_empty() {
|
||||||
|
if let Some(Symbol::BlockStart) = self.syms.pop() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unreachable!("Tried to exit global scope in symbol table")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_var(&self, name: &str) -> Result<(Type, bool)> {
|
||||||
|
for s in self.syms.iter().rev() {
|
||||||
|
if let Symbol::Var(name2, type_, mutable) = s {
|
||||||
|
if name == name2 {
|
||||||
|
return Ok((type_.clone(), *mutable));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error().reason(format!("Identifier {name} is not defined"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_type(&self, name: &str) -> Result<Type> {
|
||||||
|
for s in self.syms.iter().rev() {
|
||||||
|
if let Symbol::Type(name2, t) = s {
|
||||||
|
if name == name2 {
|
||||||
|
return Ok(t.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(p) = Primitive::from_string(name) {
|
||||||
|
return Ok(Type::Prim(p));
|
||||||
|
}
|
||||||
|
error().reason(format!("Type {name} is not defined"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn typecheck(program: Vec<Statement>) -> Vec<Statement> {
|
||||||
|
use StatementKind as s;
|
||||||
|
let mut table = SymbolTable { syms: vec![] };
|
||||||
|
let mut ret = vec![];
|
||||||
|
for s in program {
|
||||||
|
let span = s.span;
|
||||||
|
ret.push(match statement(s.into(), &mut table) {
|
||||||
|
Ok(s) => *s,
|
||||||
|
Err(e) => Statement {
|
||||||
|
kind: s::Error(e),
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
fn statement(
|
||||||
|
mut stmt: Box<Statement>,
|
||||||
|
table: &mut SymbolTable,
|
||||||
|
) -> Result<Box<Statement>> {
|
||||||
|
use Primitive as p;
|
||||||
|
use StatementKind as s;
|
||||||
|
match stmt.kind {
|
||||||
|
s::Declaration {
|
||||||
|
name,
|
||||||
|
type_str,
|
||||||
|
value,
|
||||||
|
mutable,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let type_expect = match type_str {
|
||||||
|
Some(ref s) => table.get_type(s).span(&stmt.span)?,
|
||||||
|
None => Type::Ambiguous,
|
||||||
|
};
|
||||||
|
let value = expression(value.into(), table)?;
|
||||||
|
let type_actual =
|
||||||
|
Type::coerce(&type_expect, &value.type_).reason(format!(
|
||||||
|
"Expected type '{:?}', found type '{:?}'",
|
||||||
|
type_expect, value.type_
|
||||||
|
))?;
|
||||||
|
// Check that structs are const
|
||||||
|
if let Type::Struct(_) = type_actual {
|
||||||
|
if mutable {
|
||||||
|
return error()
|
||||||
|
.reason("Struct declarations must be immutable")
|
||||||
|
.span(&stmt.span);
|
||||||
|
}
|
||||||
|
table.define_type(name.clone(), type_actual.clone());
|
||||||
|
}
|
||||||
|
// Check that functions are const
|
||||||
|
else if let Type::Function { .. } = type_actual {
|
||||||
|
if mutable {
|
||||||
|
return error()
|
||||||
|
.reason("Function declarations must be immutable")
|
||||||
|
.span(&stmt.span);
|
||||||
|
}
|
||||||
|
table.define_var(name.clone(), type_actual.clone(), false);
|
||||||
|
} else {
|
||||||
|
table.define_var(name.clone(), type_actual.clone(), mutable);
|
||||||
|
}
|
||||||
|
stmt.kind = s::Declaration {
|
||||||
|
name,
|
||||||
|
type_str,
|
||||||
|
type_actual,
|
||||||
|
value: *value,
|
||||||
|
mutable,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
s::Assignment { name, value } => {
|
||||||
|
let (type_, mutable) = table.get_var(&name).span(&stmt.span)?;
|
||||||
|
// Check that it is mutable
|
||||||
|
if !mutable {
|
||||||
|
return error()
|
||||||
|
.reason(format!("Cannot assign to immutable '{}'", name))
|
||||||
|
.span(&stmt.span);
|
||||||
|
}
|
||||||
|
let value = *expression(value.into(), table)?;
|
||||||
|
// Check for correct type
|
||||||
|
if type_ != value.type_ {
|
||||||
|
return error().reason(format!(
|
||||||
|
"Attempted to assign '{:?}' to '{type_:?}'",
|
||||||
|
value.type_
|
||||||
|
));
|
||||||
|
}
|
||||||
|
stmt.kind = s::Assignment { name, value };
|
||||||
|
},
|
||||||
|
s::If {
|
||||||
|
predicate,
|
||||||
|
block,
|
||||||
|
else_,
|
||||||
|
} => {
|
||||||
|
table.start_block();
|
||||||
|
let predicate = *expression(predicate.into(), table)?;
|
||||||
|
if predicate.type_ != Type::Prim(p::boolean) {
|
||||||
|
return error()
|
||||||
|
.reason(format!(
|
||||||
|
"Predicate of if statement must be a boolean, found {:?}",
|
||||||
|
predicate.type_
|
||||||
|
))
|
||||||
|
.span(&predicate.span);
|
||||||
|
}
|
||||||
|
let mut new_block = vec![];
|
||||||
|
for stmt in block {
|
||||||
|
new_block.push(*statement(stmt.into(), table)?);
|
||||||
|
}
|
||||||
|
let else_ = if let Some(else_) = else_ {
|
||||||
|
Some(statement(else_, table)?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
stmt.kind = s::If {
|
||||||
|
predicate,
|
||||||
|
block: new_block,
|
||||||
|
else_,
|
||||||
|
};
|
||||||
|
table.end_block();
|
||||||
|
},
|
||||||
|
s::While { predicate, block } => {
|
||||||
|
table.start_block();
|
||||||
|
let predicate = *expression(predicate.into(), table)?;
|
||||||
|
if predicate.type_ != Type::Prim(p::boolean) {
|
||||||
|
return error()
|
||||||
|
.reason(format!(
|
||||||
|
"Predicate of while statement must be a boolean, found {:?}",
|
||||||
|
predicate.type_
|
||||||
|
))
|
||||||
|
.span(&predicate.span);
|
||||||
|
}
|
||||||
|
let mut new_block = vec![];
|
||||||
|
for stmt in block {
|
||||||
|
new_block.push(*statement(stmt.into(), table)?);
|
||||||
|
}
|
||||||
|
stmt.kind = s::While {
|
||||||
|
predicate,
|
||||||
|
block: new_block,
|
||||||
|
};
|
||||||
|
table.end_block();
|
||||||
|
},
|
||||||
|
s::Print(e) => {
|
||||||
|
stmt.kind = s::Print(*expression(e.into(), table)?);
|
||||||
|
},
|
||||||
|
s::Expression(mut e) => {
|
||||||
|
use ExpressionKind as e;
|
||||||
|
let is_func = if let e::Function { params, .. } = &mut e.kind {
|
||||||
|
// TODO: start/end function instead
|
||||||
|
table.start_block();
|
||||||
|
for p in params {
|
||||||
|
p.type_actual = table.get_type(&p.type_str)?;
|
||||||
|
table.define_var(p.name.clone(), p.type_actual.clone(), false);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
stmt.kind = s::Expression(*expression(e.into(), table)?);
|
||||||
|
if is_func {
|
||||||
|
table.end_block();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
s::Block(block) => {
|
||||||
|
table.start_block();
|
||||||
|
let mut new_block = vec![];
|
||||||
|
for stmt in block {
|
||||||
|
new_block.push(*statement(stmt.into(), table)?);
|
||||||
|
}
|
||||||
|
stmt.kind = s::Block(new_block);
|
||||||
|
table.end_block();
|
||||||
|
},
|
||||||
|
s::Error(e) => return Err(e),
|
||||||
|
}
|
||||||
|
Ok(stmt)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expression(
|
||||||
|
mut expr: Box<Expression>,
|
||||||
|
table: &SymbolTable,
|
||||||
|
) -> Result<Box<Expression>> {
|
||||||
|
use ExpressionKind as e;
|
||||||
|
use Immediate as i;
|
||||||
|
use Primitive as p;
|
||||||
|
let type_ = match expr.kind {
|
||||||
|
e::Immediate(ref i) => Type::Prim(match i {
|
||||||
|
i::Integer(_) => p::integer_ambiguous,
|
||||||
|
i::Real(_) => p::real_ambiguous,
|
||||||
|
i::String(_) => p::string,
|
||||||
|
i::Boolean(_) => p::boolean,
|
||||||
|
}),
|
||||||
|
e::Identifier(ref i) => table.get_var(i)?.0,
|
||||||
|
e::Binary { op, left, right } => {
|
||||||
|
let left = expression(left, table)?;
|
||||||
|
let right = expression(right, table)?;
|
||||||
|
let type_ = Type::binary_op(&left.type_, op, &right.type_)?;
|
||||||
|
expr.kind = e::Binary { left, right, op };
|
||||||
|
type_
|
||||||
|
},
|
||||||
|
e::Unary { op, child } => {
|
||||||
|
let child = expression(child, table)?;
|
||||||
|
let type_ = Type::unary_op(op, &child.type_)?;
|
||||||
|
expr.kind = e::Unary { child, op };
|
||||||
|
type_
|
||||||
|
},
|
||||||
|
e::Parenthesis(inner) => {
|
||||||
|
let inner = expression(inner, table)?;
|
||||||
|
let type_ = inner.type_.clone();
|
||||||
|
expr.kind = e::Parenthesis(inner);
|
||||||
|
type_
|
||||||
|
},
|
||||||
|
e::Function {
|
||||||
|
mut params,
|
||||||
|
returns_str,
|
||||||
|
mut returns_actual,
|
||||||
|
body,
|
||||||
|
} => {
|
||||||
|
for p in &mut params {
|
||||||
|
p.type_actual = table.get_type(&p.type_str).span(&expr.span)?;
|
||||||
|
}
|
||||||
|
returns_actual = match &returns_str {
|
||||||
|
Some(s) => table.get_type(s).span(&expr.span)?,
|
||||||
|
None => Type::Nothing,
|
||||||
|
};
|
||||||
|
expr.kind = e::Function {
|
||||||
|
params: params.clone(),
|
||||||
|
returns_str,
|
||||||
|
returns_actual: returns_actual.clone(),
|
||||||
|
body,
|
||||||
|
};
|
||||||
|
Type::Function {
|
||||||
|
params: params.into_iter().map(|p| p.type_actual).collect(),
|
||||||
|
returns: returns_actual.into(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
e::Struct(mut params) => {
|
||||||
|
for p in &mut params {
|
||||||
|
p.type_actual = table.get_type(&p.type_str).span(&expr.span)?;
|
||||||
|
}
|
||||||
|
expr.kind = e::Struct(params.clone());
|
||||||
|
Type::Struct(params)
|
||||||
|
},
|
||||||
|
e::StructLiteral { name, mut args } => {
|
||||||
|
let type_ = table.get_type(&name).span(&expr.span)?;
|
||||||
|
let Type::Struct(params) = type_ else {
|
||||||
|
return error().reason(format!(
|
||||||
|
"Cannot construct type {:?} as struct literal",
|
||||||
|
type_
|
||||||
|
));
|
||||||
|
};
|
||||||
|
if args.len() != params.len() {
|
||||||
|
return error().reason(format!(
|
||||||
|
"Incorrect number of parameters for struct '{}', expected {} and \
|
||||||
|
found {}",
|
||||||
|
name,
|
||||||
|
params.len(),
|
||||||
|
args.len()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
// TODO out of order params
|
||||||
|
let mut new_args = vec![];
|
||||||
|
for (
|
||||||
|
(argname, argexpr),
|
||||||
|
Parameter {
|
||||||
|
name: pname,
|
||||||
|
type_actual: ptype,
|
||||||
|
..
|
||||||
|
},
|
||||||
|
) in args.iter().zip(params.iter())
|
||||||
|
{
|
||||||
|
if argname != pname {
|
||||||
|
return error()
|
||||||
|
.reason(format!(
|
||||||
|
"In struct literal, expected parameter '{pname}', found \
|
||||||
|
'{argname}'"
|
||||||
|
))
|
||||||
|
.span(&argexpr.span);
|
||||||
|
}
|
||||||
|
let argspan = argexpr.span;
|
||||||
|
let arg = *expression(argexpr.clone().into(), table)
|
||||||
|
.trace_span(expr.span, "while parsing struct literal")?;
|
||||||
|
if &arg.type_ != ptype {
|
||||||
|
return error()
|
||||||
|
.reason(format!(
|
||||||
|
"In struct literal, expected type '{ptype:?}', found '{:?}",
|
||||||
|
arg.type_,
|
||||||
|
))
|
||||||
|
.span(&argspan);
|
||||||
|
}
|
||||||
|
new_args.push((argname.clone(), arg));
|
||||||
|
}
|
||||||
|
expr.kind = e::StructLiteral {
|
||||||
|
name,
|
||||||
|
args: new_args,
|
||||||
|
};
|
||||||
|
Type::Struct(params)
|
||||||
|
},
|
||||||
|
e::Call { callee, args } => {
|
||||||
|
let callee = expression(callee, table)?;
|
||||||
|
// Check that this is actually a function
|
||||||
|
let Type::Function {
|
||||||
|
ref params,
|
||||||
|
ref returns,
|
||||||
|
} = callee.type_
|
||||||
|
else {
|
||||||
|
return error()
|
||||||
|
.reason(format!("Cannot call type {:?}", callee.type_))
|
||||||
|
.span(&callee.span);
|
||||||
|
};
|
||||||
|
// Check for correct number of args
|
||||||
|
if params.len() != args.len() {
|
||||||
|
return error()
|
||||||
|
.reason(format!(
|
||||||
|
"Wrong number of arguments, function expects {}",
|
||||||
|
params.len()
|
||||||
|
))
|
||||||
|
.span(&callee.span);
|
||||||
|
}
|
||||||
|
// Check for correct arg types
|
||||||
|
for (expect, actual) in params.iter().zip(args.iter()) {
|
||||||
|
if *expect != actual.type_ {
|
||||||
|
return error()
|
||||||
|
.reason(format!(
|
||||||
|
"Expected type {expect:?}, found {:?}",
|
||||||
|
actual.type_
|
||||||
|
))
|
||||||
|
.span(&actual.span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let returns = *returns.clone();
|
||||||
|
expr.kind = e::Call { callee, args };
|
||||||
|
returns
|
||||||
|
},
|
||||||
|
e::Field { namespace, field } => {
|
||||||
|
let namespace = expression(namespace, table)?;
|
||||||
|
// Check that namespace is struct
|
||||||
|
// TODO: fields in other types
|
||||||
|
let Type::Struct(ref params) = namespace.type_ else {
|
||||||
|
return error()
|
||||||
|
.reason(format!("Type {:?} does not have fields", namespace.type_))
|
||||||
|
.span(&namespace.span);
|
||||||
|
};
|
||||||
|
// Check that field is identifier
|
||||||
|
// TODO: tuple fields?
|
||||||
|
let e::Identifier(ref name) = field.kind else {
|
||||||
|
return error()
|
||||||
|
.reason("Field must be an identifier")
|
||||||
|
.span(&field.span);
|
||||||
|
};
|
||||||
|
let mut type_ = None;
|
||||||
|
for p in params {
|
||||||
|
if &p.name == name {
|
||||||
|
type_ = Some(p.type_actual.clone());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let type_ = type_
|
||||||
|
.reason(format!(
|
||||||
|
"Type {:?} does not contain field {}",
|
||||||
|
namespace, name
|
||||||
|
))
|
||||||
|
.span(&field.span)?;
|
||||||
|
expr.kind = e::Field { namespace, field };
|
||||||
|
type_
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expr.type_ = type_;
|
||||||
|
Ok(expr)
|
||||||
|
}
|
|
@ -577,7 +577,9 @@ fn bake_string(s: &str) -> Result<String> {
|
||||||
};
|
};
|
||||||
a().reason("Found invalid Unicode (\\uXXXX) escape sequence")?
|
a().reason("Found invalid Unicode (\\uXXXX) escape sequence")?
|
||||||
},
|
},
|
||||||
_ => return Err(Diagnostic::new("Found invalid escape sequence")),
|
_ => {
|
||||||
|
return Err(Diagnostic::new("Found invalid escape sequence", None));
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
// Unremarkable character
|
// Unremarkable character
|
||||||
Some(c) => baked.push(c),
|
Some(c) => baked.push(c),
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use crate::err::*;
|
/*
|
||||||
|
use crate::{err::*, BinaryOp, UnaryOp};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{Expression, ExpressionKind, Statement, StatementKind};
|
||||||
Expression, ExpressionKind, Statement, StatementKind, Token, TokenKind,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum Value {
|
enum Value {
|
||||||
|
@ -106,31 +105,31 @@ impl<I: Iterator<Item = Statement>> Interpreter<I> {
|
||||||
|
|
||||||
fn evaluate_unary(
|
fn evaluate_unary(
|
||||||
&mut self,
|
&mut self,
|
||||||
token: TokenKind,
|
token: UnaryOp,
|
||||||
child: Expression,
|
child: Expression,
|
||||||
) -> Result<Value> {
|
) -> Result<Value> {
|
||||||
use TokenKind as t;
|
use UnaryOp as u;
|
||||||
use Value as v;
|
use Value as v;
|
||||||
let val = self.evaluate(child)?;
|
let val = self.evaluate(child)?;
|
||||||
Ok(match val {
|
Ok(match val {
|
||||||
v::Integer(i) => v::Integer(match token {
|
v::Integer(i) => v::Integer(match token {
|
||||||
t::Plus => i,
|
u::Plus => i,
|
||||||
t::Minus => -i,
|
u::Minus => -i,
|
||||||
_ => {
|
_ => {
|
||||||
return error()
|
return error()
|
||||||
.reason(format!("Unary {token:?} is undefined for integers"));
|
.reason(format!("Unary {token:?} is undefined for integers"));
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
v::Real(r) => v::Real(match token {
|
v::Real(r) => v::Real(match token {
|
||||||
t::Plus => r,
|
u::Plus => r,
|
||||||
t::Minus => -r,
|
u::Minus => -r,
|
||||||
_ => {
|
_ => {
|
||||||
return error()
|
return error()
|
||||||
.reason(format!("Unary {token:?} is undefined for reals"));
|
.reason(format!("Unary {token:?} is undefined for reals"));
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
v::Boolean(b) => v::Boolean(match token {
|
v::Boolean(b) => v::Boolean(match token {
|
||||||
t::Not => !b,
|
u::Not => !b,
|
||||||
_ => {
|
_ => {
|
||||||
return error()
|
return error()
|
||||||
.reason(format!("Unary {token:?} is undefined for booleans"));
|
.reason(format!("Unary {token:?} is undefined for booleans"));
|
||||||
|
@ -145,36 +144,36 @@ impl<I: Iterator<Item = Statement>> Interpreter<I> {
|
||||||
|
|
||||||
fn evaluate_binary(
|
fn evaluate_binary(
|
||||||
&mut self,
|
&mut self,
|
||||||
token: TokenKind,
|
token: BinaryOp,
|
||||||
left: Expression,
|
left: Expression,
|
||||||
right: Expression,
|
right: Expression,
|
||||||
) -> Result<Value> {
|
) -> Result<Value> {
|
||||||
use TokenKind as t;
|
use BinaryOp as b;
|
||||||
use Value::*;
|
use Value::*;
|
||||||
let left = self.evaluate(left)?;
|
let left = self.evaluate(left)?;
|
||||||
let right = self.evaluate(right)?;
|
let right = self.evaluate(right)?;
|
||||||
Ok(match (left.clone(), right.clone()) {
|
Ok(match (left.clone(), right.clone()) {
|
||||||
(Integer(l), Integer(r)) => match token {
|
(Integer(l), Integer(r)) => match token {
|
||||||
t::Plus => Integer(l + r),
|
b::Plus => Integer(l + r),
|
||||||
t::Minus => Integer(l - r),
|
b::Minus => Integer(l - r),
|
||||||
t::Star => Integer(l * r),
|
b::Star => Integer(l * r),
|
||||||
t::Slash => Integer(l / r),
|
b::Slash => Integer(l / r),
|
||||||
t::Percent => Integer(l % r),
|
b::Percent => Integer(l % r),
|
||||||
t::DoubleEqual => Boolean(l == r),
|
b::DoubleEqual => Boolean(l == r),
|
||||||
t::Less => Boolean(l < r),
|
b::Less => Boolean(l < r),
|
||||||
t::Greater => Boolean(l > r),
|
b::Greater => Boolean(l > r),
|
||||||
t::LessEqual => Boolean(l <= r),
|
b::LessEqual => Boolean(l <= r),
|
||||||
t::GreaterEqual => Boolean(l >= r),
|
b::GreaterEqual => Boolean(l >= r),
|
||||||
t => {
|
t => {
|
||||||
return error()
|
return error()
|
||||||
.reason(format!("Binary {t:?} is undefined for integers"));
|
.reason(format!("Binary {t:?} is undefined for integers"));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
(Real(l), Real(r)) => Real(match token {
|
(Real(l), Real(r)) => Real(match token {
|
||||||
t::Plus => l + r,
|
b::Plus => l + r,
|
||||||
t::Minus => l - r,
|
b::Minus => l - r,
|
||||||
t::Star => l * r,
|
b::Star => l * r,
|
||||||
t::Slash => l / r,
|
b::Slash => l / r,
|
||||||
t => {
|
t => {
|
||||||
return error()
|
return error()
|
||||||
.reason(format!("Binary {t:?} is undefined for reals"));
|
.reason(format!("Binary {t:?} is undefined for reals"));
|
||||||
|
@ -190,17 +189,16 @@ impl<I: Iterator<Item = Statement>> Interpreter<I> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn evaluate(&mut self, expr: Expression) -> Result<Value> {
|
fn evaluate(&mut self, expr: Expression) -> Result<Value> {
|
||||||
|
use crate::Immediate as im;
|
||||||
use ExpressionKind as e;
|
use ExpressionKind as e;
|
||||||
match expr.kind {
|
match expr.kind {
|
||||||
e::Integer(i) => Ok(Value::Integer(i)),
|
e::Immediate(im::Integer(i)) => Ok(Value::Integer(i)),
|
||||||
e::Real(r) => Ok(Value::Real(r)),
|
e::Immediate(im::Real(r)) => Ok(Value::Real(r)),
|
||||||
e::String(s) => Ok(Value::String(s)),
|
e::Immediate(im::String(s)) => Ok(Value::String(s)),
|
||||||
e::Boolean(b) => Ok(Value::Boolean(b)),
|
e::Immediate(im::Boolean(b)) => Ok(Value::Boolean(b)),
|
||||||
e::Identifier(i) => self.scope.access(i),
|
e::Identifier(i) => self.scope.access(i),
|
||||||
e::Binary { token, left, right } => {
|
e::Binary { op, left, right } => self.evaluate_binary(op, *left, *right),
|
||||||
self.evaluate_binary(token, *left, *right)
|
e::Unary { op, child } => self.evaluate_unary(op, *child),
|
||||||
},
|
|
||||||
e::Unary { token, child } => self.evaluate_unary(token, *child),
|
|
||||||
e::Parenthesis(e) => self.evaluate(*e),
|
e::Parenthesis(e) => self.evaluate(*e),
|
||||||
e::Call { callee, args } => todo!(),
|
e::Call { callee, args } => todo!(),
|
||||||
e::Field { namespace, field } => todo!(),
|
e::Field { namespace, field } => todo!(),
|
||||||
|
@ -209,6 +207,7 @@ impl<I: Iterator<Item = Statement>> Interpreter<I> {
|
||||||
returns,
|
returns,
|
||||||
body,
|
body,
|
||||||
} => todo!(),
|
} => todo!(),
|
||||||
|
e::Struct(_) => todo!(),
|
||||||
}
|
}
|
||||||
.span(&expr.span)
|
.span(&expr.span)
|
||||||
}
|
}
|
||||||
|
@ -216,14 +215,13 @@ impl<I: Iterator<Item = Statement>> Interpreter<I> {
|
||||||
pub fn execute(&mut self, statement: Statement) -> Result<()> {
|
pub fn execute(&mut self, statement: Statement) -> Result<()> {
|
||||||
use StatementKind as s;
|
use StatementKind as s;
|
||||||
match statement.kind {
|
match statement.kind {
|
||||||
s::Mutable { name, value, .. } => {
|
s::Declaration {
|
||||||
self.scope.declare(name.clone())?;
|
name,
|
||||||
if let Some(value) = value {
|
type_str,
|
||||||
let value = self.evaluate(value)?;
|
type_actual,
|
||||||
self.scope.assign(name, value)?;
|
value,
|
||||||
}
|
mutable,
|
||||||
},
|
} => {
|
||||||
s::Immutable { name, value, .. } => {
|
|
||||||
self.scope.declare(name.clone())?;
|
self.scope.declare(name.clone())?;
|
||||||
let value = self.evaluate(value)?;
|
let value = self.evaluate(value)?;
|
||||||
self.scope.assign(name, value)?;
|
self.scope.assign(name, value)?;
|
||||||
|
@ -304,3 +302,4 @@ impl<I: Iterator<Item = Statement>> Interpreter<I> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
136
src/types.rs
136
src/types.rs
|
@ -1,136 +0,0 @@
|
||||||
use crate::Expression;
|
|
||||||
|
|
||||||
use super::err::*;
|
|
||||||
|
|
||||||
macro_rules! primitives {
|
|
||||||
( $($i:ident),* ) => {
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
#[allow(non_camel_case_types, dead_code)]
|
|
||||||
pub enum Primitive {
|
|
||||||
whole_ambiguous,
|
|
||||||
integer_ambiguous,
|
|
||||||
real_ambiguous,
|
|
||||||
$($i,)*
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Primitive {
|
|
||||||
pub fn from_string(string: &'static str) -> Option<Self> {
|
|
||||||
match string {
|
|
||||||
$(stringify!{$i} => Some(Self::$i),)*
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl std::fmt::Display for Primitive {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Primitive::whole_ambiguous => write!(f, "<ambiguous whole>"),
|
|
||||||
Primitive::integer_ambiguous => write!(f, "<ambiguous integer>"),
|
|
||||||
Primitive::real_ambiguous => write!(f, "<ambiguous real>"),
|
|
||||||
$(Primitive::$i => write!(f, stringify!{$i}),)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
primitives! {
|
|
||||||
w8, w16, w32, w64, whole,
|
|
||||||
i8, i16, i32, i64, integer,
|
|
||||||
r32, r64, real,
|
|
||||||
boolean,
|
|
||||||
string, glyph
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Primitive {
|
|
||||||
pub fn coerce(a: Self, b: Self) -> Result<Self> {
|
|
||||||
use Primitive::*;
|
|
||||||
match (a, b) {
|
|
||||||
// Whole? coerces to any whole or integer
|
|
||||||
(whole_ambiguous, w @ (w8 | w16 | w32 | w64 | i8 | i16 | i32 | i64))
|
|
||||||
| (w @ (w8 | w16 | w32 | w64 | i8 | i16 | i32 | i64), whole_ambiguous) => Ok(w),
|
|
||||||
// Integer? coerces to any integer
|
|
||||||
(integer_ambiguous, i @ (i8 | i16 | i32 | i64))
|
|
||||||
| (i @ (i8 | i16 | i32 | i64), integer_ambiguous) => Ok(i),
|
|
||||||
_ => error(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use Primitive as p;
|
|
||||||
|
|
||||||
// Implement math operations for regular types
|
|
||||||
macro_rules! selfsame_op {
|
|
||||||
($trait:ident, $fn:ident, $($i:ident),* ) => {
|
|
||||||
impl std::ops::$trait for Primitive {
|
|
||||||
type Output = Result<Primitive>;
|
|
||||||
fn $fn(self, rhs: Self) -> Self::Output {
|
|
||||||
match (self, rhs) {
|
|
||||||
$((p::$i, p::$i) => Ok(p::$i),)*
|
|
||||||
_ => error()
|
|
||||||
.reason(format!("Operation not defined for primitives {} and {}", self, rhs))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement all regular math
|
|
||||||
macro_rules! all_selfsame {
|
|
||||||
($($i:ident),*) => {
|
|
||||||
selfsame_op!(Add, add, $($i),*);
|
|
||||||
selfsame_op!(Sub, sub, $($i),*);
|
|
||||||
selfsame_op!(Mul, mul, $($i),*);
|
|
||||||
selfsame_op!(Div, div, $($i),*);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
all_selfsame!(w8, w16, w32, w64, whole, i8, i16, i32, i64, integer, r32, r64, real, string);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Type {
|
|
||||||
Ambiguous,
|
|
||||||
Empty,
|
|
||||||
Primitive(Primitive),
|
|
||||||
Struct(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Symbol {
|
|
||||||
Mutable {
|
|
||||||
name: String,
|
|
||||||
value: Option<Expression>,
|
|
||||||
type_: Type,
|
|
||||||
},
|
|
||||||
Immutable {
|
|
||||||
name: String,
|
|
||||||
value: Expression,
|
|
||||||
type_: Type,
|
|
||||||
},
|
|
||||||
Struct {
|
|
||||||
name: String,
|
|
||||||
},
|
|
||||||
BlockStart,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct SymbolTable {
|
|
||||||
stack: Vec<Symbol>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SymbolTable {
|
|
||||||
pub fn define(&mut self, sym: Symbol) {
|
|
||||||
self.stack.push(sym);
|
|
||||||
}
|
|
||||||
pub fn start_block(&mut self) {
|
|
||||||
self.stack.push(Symbol::BlockStart);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn end_block(&mut self) {
|
|
||||||
while !self.stack.is_empty() {
|
|
||||||
if let Some(Symbol::BlockStart) = self.stack.pop() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue