diff --git a/Cargo.lock b/Cargo.lock
index afaae83..4dfe71f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,6 +2,103 @@
# It is not intended for manual editing.
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]]
name = "lang"
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",
+]
diff --git a/Cargo.toml b/Cargo.toml
index d6cff4b..5e31e90 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,3 +4,4 @@ version = "0.1.0"
edition = "2021"
[dependencies]
+wat = "1.219"
diff --git a/demo.lang b/demo.lang
index 790ccc9..88efc5d 100644
--- a/demo.lang
+++ b/demo.lang
@@ -1,6 +1,3 @@
-i := 0;
-
-while i < 10 {
- i = i + 1;
- print;
-}
+Point :: struct {x: integer};
+a : integer : 10;
+p :: Point{x: a};
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..9e19deb
--- /dev/null
+++ b/index.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/err.rs b/src/err.rs
index cbf2bd9..9cb15c5 100644
--- a/src/err.rs
+++ b/src/err.rs
@@ -3,7 +3,7 @@ use crate::Span;
pub type Result = std::result::Result;
pub fn error() -> Result {
- Err(Diagnostic::new(""))
+ Err(Diagnostic::new("", None))
}
#[derive(Clone)]
@@ -14,10 +14,10 @@ pub struct Diagnostic {
}
impl Diagnostic {
- pub fn new(reason: impl Into) -> Self {
+ pub fn new(reason: impl Into, span: Option) -> Self {
Self {
reason: reason.into(),
- span: None,
+ span,
backtrace: vec![],
}
}
@@ -58,17 +58,19 @@ impl From for Diagnostic {
use std::num::IntErrorKind::*;
match value.kind() {
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"),
- _ => Diagnostic::new("Integer value could not be parsed"),
+ InvalidDigit => {
+ Diagnostic::new("Integer value containts invalid digits", None)
+ },
+ _ => Diagnostic::new("Integer value could not be parsed", None),
}
}
}
impl From for Diagnostic {
fn from(_value: std::num::ParseFloatError) -> Self {
- Diagnostic::new("Float value could not be parsed")
+ Diagnostic::new("Float value could not be parsed", None)
}
}
diff --git a/src/frontend.rs b/src/frontend.rs
index 09962a4..f556e24 100644
--- a/src/frontend.rs
+++ b/src/frontend.rs
@@ -1,15 +1,12 @@
use std::path::Path;
-use crate::{
- err::*, treewalk::Interpreter, Parser, Statement, StatementKind, Token,
- Tokenizer,
-};
+use crate::{err::*, semantic::typecheck, Parser, Statement, Tokenizer};
#[derive(Debug, Clone)]
pub struct Module {
file_name: String,
source: String,
- ast: Vec,
+ pub program: Vec,
errors: Vec,
}
@@ -28,19 +25,13 @@ impl Module {
pub fn from_string(file_name: String, source: String) -> Self {
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![];
- for statement in Parser::new(tokens) {
- use StatementKind as s;
- match statement.kind {
- s::Error(e) => errors.push(e),
- _ => ast.push(statement),
- }
- }
Self {
file_name,
source: source.into(),
- ast,
+ program,
errors,
}
}
@@ -52,16 +43,4 @@ impl Module {
pub fn ok(&self) -> bool {
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();
- }
}
diff --git a/src/main.rs b/src/main.rs
index 79e4c2f..b977389 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,9 +2,9 @@ mod err;
mod frontend;
mod lookahead;
mod parse;
+mod semantic;
mod token;
mod treewalk;
-mod types;
use std::ops::Add;
use err::*;
@@ -63,6 +63,8 @@ fn test_expression(expr: &str) {
fn main() -> Result<()> {
let module = frontend::Module::from_file("./demo.lang")?;
- module.execute();
+ for s in &module.program {
+ println!("{s:?}");
+ }
Ok(())
}
diff --git a/src/parse/expression.rs b/src/parse/expression.rs
index c9f6e24..39ab44c 100644
--- a/src/parse/expression.rs
+++ b/src/parse/expression.rs
@@ -1,39 +1,53 @@
+use crate::semantic::*;
+
use super::*;
#[derive(Clone)]
pub struct Parameter {
- name: String,
- type_: String,
+ pub name: String,
+ pub type_str: String,
+ pub type_actual: Type,
}
impl std::fmt::Debug for Parameter {
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)]
-pub enum ExpressionKind {
+#[derive(Debug, Clone)]
+pub enum Immediate {
Integer(i64),
Real(f64),
String(String),
Boolean(bool),
+}
+
+#[derive(Clone)]
+pub enum ExpressionKind {
+ Immediate(Immediate),
Identifier(String),
Binary {
- token: TokenKind,
+ op: BinaryOp,
left: Box,
right: Box,
},
Unary {
- token: TokenKind,
+ op: UnaryOp,
child: Box,
},
Parenthesis(Box),
Function {
params: Vec,
- returns: Option,
+ returns_str: Option,
+ returns_actual: Type,
body: Vec,
},
+ Struct(Vec),
+ StructLiteral {
+ name: String,
+ args: Vec<(String, Expression)>,
+ },
Call {
callee: Box,
args: Vec,
@@ -48,39 +62,55 @@ pub enum ExpressionKind {
pub struct Expression {
pub kind: ExpressionKind,
pub span: Span,
+ pub type_: Type,
}
impl Expression {
pub fn new(kind: ExpressionKind, span: Span) -> Self {
- Self { kind, span }
+ Self {
+ kind,
+ span,
+ type_: Type::Ambiguous,
+ }
}
}
impl std::fmt::Debug for ExpressionKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use ExpressionKind as e;
+ use Immediate as im;
match self {
- e::Integer(i) => write!(f, "{i}"),
- e::Binary { token, left, right } => {
+ e::Immediate(i) => match i {
+ 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:?})")
},
e::Parenthesis(inner) => write!(f, "{inner:?}"),
- e::Unary { token, child } => {
+ e::Unary { op: 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::Boolean(b) => write!(f, "{b}"),
e::Call { callee, args } => write!(f, "({callee:?} call {args:?})"),
e::Field { namespace, field } => {
write!(f, "({namespace:?} . {field:?})")
},
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> Parser {
use TokenKind as t;
let next = self.peek(0)?;
// Unary prefix expression
- let mut current = if let Ok(p) = unary_prefix_prec(&next.0) {
- let operator = self.next_tok().expect("unreachable");
+ let mut current = if let Ok(operator) = UnaryOp::try_from(&next.0) {
+ 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
- .expression(p)
- .trace(format!("while parsing unary {}", operator.0))
- .span(&operator.1)?;
- let span = child.span + operator.1;
+ .expression(operator.precedence())
+ .trace(format!("while parsing unary {}", operator))
+ .span(&span)?;
+ let span = span + child.span;
Expression::new(
e::Unary {
- token: operator.0,
+ op: operator,
child: child.into(),
},
span,
@@ -120,21 +156,23 @@ impl> Parser {
// Precedence climbing loop
while let Ok(next) = self.peek(0) {
// Binary infix
- if let Ok((new_precedence, left_assoc)) = binary_prec(&next.0) {
- if (!left_assoc && new_precedence <= precedence)
+ if let Ok(operator) = BinaryOp::try_from(&next.0) {
+ let new_precedence = operator.precedence();
+ if ((operator.assoc() == LEFT_ASSOC) && new_precedence <= precedence)
|| (new_precedence < precedence)
{
return Ok(current);
}
- let operator = self.next_tok().expect("unreachable");
+ self.skip(1);
+ let span = next.1;
let rhs = self
.expression(new_precedence)
- .trace(format!("while parsing binary {}", operator.0))
- .span(&operator.1)?;
+ .trace(format!("while parsing binary {}", operator))
+ .span(&span)?;
let span = next.1 + rhs.span;
current = Expression::new(
e::Binary {
- token: operator.0,
+ op: operator,
left: current.into(),
right: rhs.into(),
},
@@ -187,12 +225,20 @@ impl> Parser {
);
}
// Unary postfix
- else if let Ok(_) = unary_postfix_prec(&next.0) {
- let operator = self.next_tok().expect("unreachable");
- let span = next.1 + operator.1;
+ else if let Ok(operator) = UnaryOp::try_from(&next.0) {
+ self.skip(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(
e::Unary {
- token: operator.0,
+ op: operator,
child: current.into(),
},
span,
@@ -206,16 +252,32 @@ impl> Parser {
fn primary(&mut self) -> Result {
use ExpressionKind as e;
+ use Immediate as im;
use TokenKind as t;
let next = self.peek(0)?;
let mut span = next.1;
let kind = match next.0 {
- t::IntegerLiteral(i) => e::Integer(i),
- t::FloatLiteral(f) => e::Real(f),
- t::StringLiteral(s) => e::String(s),
- t::True => e::Boolean(true),
- t::Identifier(i) => e::Identifier(i),
- // function
+ t::IntegerLiteral(i) => {
+ self.skip(1);
+ e::Immediate(im::Integer(i))
+ },
+ t::FloatLiteral(f) => {
+ 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
if (self.look(1, t::Identifier("".into())).is_ok()
&& self.look(2, t::Colon).is_ok())
@@ -236,7 +298,11 @@ impl> Parser {
.identifier()
.trace_span(span, "while parsing function parameter type")?;
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() {
break;
}
@@ -245,7 +311,7 @@ impl> Parser {
.eat(t::RightParen)
.span(&span)
.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;
let (identifier, span2) = self
.identifier()
@@ -262,18 +328,78 @@ impl> Parser {
span = span + span2;
e::Function {
params,
- returns,
+ returns_str,
+ returns_actual: Type::Ambiguous,
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 => {
self.skip(1);
let expr = self
.expression(0)
.trace("while parsing parenthesized expression")?;
self
- .look(0, t::RightParen)
+ .eat(t::RightParen)
.reason("Unclosed '('")
.span(&expr.span)?;
e::Parenthesis(expr.into())
@@ -284,8 +410,7 @@ impl> Parser {
.reason(format!("Expected expression, found {}", next.0));
},
};
- self.skip(1);
- Ok(Expression { kind, span })
+ Ok(Expression::new(kind, span))
}
fn identifier(&mut self) -> Result<(String, Span)> {
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index e425b5b..0ee6a47 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -8,51 +8,82 @@ use crate::{Span, Token, TokenKind};
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 {
+ 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 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 {
- 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 {
- 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;
type TokenIter = crate::Window;
@@ -146,8 +177,7 @@ impl> Parser {
let mut span = self.eat(t::LeftBrace).reason("Expected block")?.1;
let mut statements = vec![];
loop {
- let next = self.peek(0)?;
- span = span + next.1;
+ span = span + self.peek(0)?.1;
match self.eat(t::RightBrace) {
Ok(t) => {
span = span + t.1;
diff --git a/src/parse/statement.rs b/src/parse/statement.rs
index c990185..fde53ab 100644
--- a/src/parse/statement.rs
+++ b/src/parse/statement.rs
@@ -1,16 +1,15 @@
+use crate::semantic::Type;
+
use super::*;
#[derive(Debug, Clone)]
pub enum StatementKind {
- Mutable {
+ Declaration {
name: String,
- type_: Option,
- value: Option,
- },
- Immutable {
- name: String,
- type_: Option,
+ type_str: Option,
+ type_actual: Type,
value: Expression,
+ mutable: bool,
},
Assignment {
name: String,
@@ -49,44 +48,47 @@ impl> Parser {
(Token(t::Identifier(name), span2), Ok(Token(t::Colon, span3))) => {
self.skip(2);
span = span + span2 + span3;
- // type
- let type_ = match self.eat(t::Identifier("".into())) {
+ let type_str = match self.eat(t::Identifier("".into())) {
Ok(Token(t::Identifier(s), span2)) => {
span = span + span2;
Some(s)
},
_ => None,
};
- // value
- match self.eat(t::Equal).or_else(|_| self.eat(t::Colon)) {
- Ok(Token(t::Colon, span2)) => {
- span = span + span2;
- let value = self
- .expression(0)
- .trace_span(span, "while parsing mutable declaration")?;
- span = span + value.span;
- Statement {
- kind: s::Immutable { name, type_, value },
- span,
- }
+ let mutable = if self.eat(t::Equal).is_ok() {
+ true
+ } else if self.eat(t::Colon).is_ok() {
+ false
+ } else {
+ return error()
+ .reason(format!("Declaration of '{}' must be initialized", name))
+ .span(&span);
+ };
+ let value = self
+ .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 + span2;
- let value = self
- .expression(0)
- .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),
+ span,
+ };
+ if no_semicolon {
+ return Ok(s);
}
+ s
},
// Assignment
(Token(t::Identifier(name), span2), Ok(Token(t::Equal, span3))) => {
diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs
new file mode 100644
index 0000000..d72249e
--- /dev/null
+++ b/src/semantic/mod.rs
@@ -0,0 +1,5 @@
+mod primitives;
+mod types;
+
+pub use primitives::*;
+pub use types::*;
diff --git a/src/semantic/primitives.rs b/src/semantic/primitives.rs
new file mode 100644
index 0000000..7be5272
--- /dev/null
+++ b/src/semantic/primitives.rs
@@ -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 {
+ 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, ""),
+ Primitive::real_ambiguous => write!(f, ""),
+ $(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 {
+ 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 {
+ 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)),
+ }
+ }
+}
diff --git a/src/semantic/types.rs b/src/semantic/types.rs
new file mode 100644
index 0000000..e1a4557
--- /dev/null
+++ b/src/semantic/types.rs
@@ -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),
+ Function {
+ params: Vec,
+ returns: Box,
+ },
+}
+
+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 {
+ 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 {
+ 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 {
+ 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,
+}
+
+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 {
+ 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) -> Vec {
+ 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,
+ table: &mut SymbolTable,
+) -> Result> {
+ 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,
+ table: &SymbolTable,
+) -> Result> {
+ 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)
+}
diff --git a/src/token.rs b/src/token.rs
index 3bbb0a7..44714e9 100644
--- a/src/token.rs
+++ b/src/token.rs
@@ -577,7 +577,9 @@ fn bake_string(s: &str) -> Result {
};
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
Some(c) => baked.push(c),
diff --git a/src/treewalk.rs b/src/treewalk.rs
index 0b07252..f95de43 100644
--- a/src/treewalk.rs
+++ b/src/treewalk.rs
@@ -1,9 +1,8 @@
-use crate::err::*;
+/*
+use crate::{err::*, BinaryOp, UnaryOp};
use std::collections::HashMap;
-use crate::{
- Expression, ExpressionKind, Statement, StatementKind, Token, TokenKind,
-};
+use crate::{Expression, ExpressionKind, Statement, StatementKind};
#[derive(Debug, Clone)]
enum Value {
@@ -106,31 +105,31 @@ impl> Interpreter {
fn evaluate_unary(
&mut self,
- token: TokenKind,
+ token: UnaryOp,
child: Expression,
) -> Result {
- use TokenKind as t;
+ use UnaryOp as u;
use Value as v;
let val = self.evaluate(child)?;
Ok(match val {
v::Integer(i) => v::Integer(match token {
- t::Plus => i,
- t::Minus => -i,
+ u::Plus => i,
+ u::Minus => -i,
_ => {
return error()
.reason(format!("Unary {token:?} is undefined for integers"));
},
}),
v::Real(r) => v::Real(match token {
- t::Plus => r,
- t::Minus => -r,
+ u::Plus => r,
+ u::Minus => -r,
_ => {
return error()
.reason(format!("Unary {token:?} is undefined for reals"));
},
}),
v::Boolean(b) => v::Boolean(match token {
- t::Not => !b,
+ u::Not => !b,
_ => {
return error()
.reason(format!("Unary {token:?} is undefined for booleans"));
@@ -145,36 +144,36 @@ impl> Interpreter {
fn evaluate_binary(
&mut self,
- token: TokenKind,
+ token: BinaryOp,
left: Expression,
right: Expression,
) -> Result {
- use TokenKind as t;
+ use BinaryOp as b;
use Value::*;
let left = self.evaluate(left)?;
let right = self.evaluate(right)?;
Ok(match (left.clone(), right.clone()) {
(Integer(l), Integer(r)) => match token {
- t::Plus => Integer(l + r),
- t::Minus => Integer(l - r),
- t::Star => Integer(l * r),
- t::Slash => Integer(l / r),
- t::Percent => Integer(l % r),
- t::DoubleEqual => Boolean(l == r),
- t::Less => Boolean(l < r),
- t::Greater => Boolean(l > r),
- t::LessEqual => Boolean(l <= r),
- t::GreaterEqual => Boolean(l >= r),
+ b::Plus => Integer(l + r),
+ b::Minus => Integer(l - r),
+ b::Star => Integer(l * r),
+ b::Slash => Integer(l / r),
+ b::Percent => Integer(l % r),
+ b::DoubleEqual => Boolean(l == r),
+ b::Less => Boolean(l < r),
+ b::Greater => Boolean(l > r),
+ b::LessEqual => Boolean(l <= r),
+ b::GreaterEqual => Boolean(l >= r),
t => {
return error()
.reason(format!("Binary {t:?} is undefined for integers"));
},
},
(Real(l), Real(r)) => Real(match token {
- t::Plus => l + r,
- t::Minus => l - r,
- t::Star => l * r,
- t::Slash => l / r,
+ b::Plus => l + r,
+ b::Minus => l - r,
+ b::Star => l * r,
+ b::Slash => l / r,
t => {
return error()
.reason(format!("Binary {t:?} is undefined for reals"));
@@ -190,17 +189,16 @@ impl> Interpreter {
}
fn evaluate(&mut self, expr: Expression) -> Result {
+ use crate::Immediate as im;
use ExpressionKind as e;
match expr.kind {
- e::Integer(i) => Ok(Value::Integer(i)),
- e::Real(r) => Ok(Value::Real(r)),
- e::String(s) => Ok(Value::String(s)),
- e::Boolean(b) => Ok(Value::Boolean(b)),
+ e::Immediate(im::Integer(i)) => Ok(Value::Integer(i)),
+ e::Immediate(im::Real(r)) => Ok(Value::Real(r)),
+ e::Immediate(im::String(s)) => Ok(Value::String(s)),
+ e::Immediate(im::Boolean(b)) => Ok(Value::Boolean(b)),
e::Identifier(i) => self.scope.access(i),
- e::Binary { token, left, right } => {
- self.evaluate_binary(token, *left, *right)
- },
- e::Unary { token, child } => self.evaluate_unary(token, *child),
+ e::Binary { op, left, right } => self.evaluate_binary(op, *left, *right),
+ e::Unary { op, child } => self.evaluate_unary(op, *child),
e::Parenthesis(e) => self.evaluate(*e),
e::Call { callee, args } => todo!(),
e::Field { namespace, field } => todo!(),
@@ -209,6 +207,7 @@ impl> Interpreter {
returns,
body,
} => todo!(),
+ e::Struct(_) => todo!(),
}
.span(&expr.span)
}
@@ -216,14 +215,13 @@ impl> Interpreter {
pub fn execute(&mut self, statement: Statement) -> Result<()> {
use StatementKind as s;
match statement.kind {
- s::Mutable { name, value, .. } => {
- self.scope.declare(name.clone())?;
- if let Some(value) = value {
- let value = self.evaluate(value)?;
- self.scope.assign(name, value)?;
- }
- },
- s::Immutable { name, value, .. } => {
+ s::Declaration {
+ name,
+ type_str,
+ type_actual,
+ value,
+ mutable,
+ } => {
self.scope.declare(name.clone())?;
let value = self.evaluate(value)?;
self.scope.assign(name, value)?;
@@ -304,3 +302,4 @@ impl> Interpreter {
Ok(())
}
}
+*/
diff --git a/src/types.rs b/src/types.rs
deleted file mode 100644
index 5601c7d..0000000
--- a/src/types.rs
+++ /dev/null
@@ -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 {
- 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, ""),
- Primitive::integer_ambiguous => write!(f, ""),
- Primitive::real_ambiguous => write!(f, ""),
- $(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 {
- 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;
- 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,
- type_: Type,
- },
- Immutable {
- name: String,
- value: Expression,
- type_: Type,
- },
- Struct {
- name: String,
- },
- BlockStart,
-}
-
-#[derive(Debug, Clone)]
-pub struct SymbolTable {
- stack: Vec,
-}
-
-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;
- }
- }
- }
-}
diff --git a/test.wasm b/test.wasm
new file mode 100644
index 0000000..cb1cd8b
Binary files /dev/null and b/test.wasm differ