From 47c6ccfa6b7a6b241b6f8cd0e30bb6d229436c61 Mon Sep 17 00:00:00 2001 From: Logan Date: Tue, 22 Oct 2024 11:27:39 -0500 Subject: [PATCH] added structs --- Cargo.lock | 97 +++++++ Cargo.toml | 1 + demo.lang | 9 +- index.html | 23 ++ src/err.rs | 16 +- src/frontend.rs | 31 +-- src/main.rs | 6 +- src/parse/expression.rs | 219 ++++++++++++---- src/parse/mod.rs | 118 +++++---- src/parse/statement.rs | 76 +++--- src/semantic/mod.rs | 5 + src/semantic/primitives.rs | 172 ++++++++++++ src/semantic/types.rs | 522 +++++++++++++++++++++++++++++++++++++ src/token.rs | 4 +- src/treewalk.rs | 85 +++--- src/types.rs | 136 ---------- test.wasm | Bin 0 -> 85 bytes 17 files changed, 1171 insertions(+), 349 deletions(-) create mode 100644 index.html create mode 100644 src/semantic/mod.rs create mode 100644 src/semantic/primitives.rs create mode 100644 src/semantic/types.rs delete mode 100644 src/types.rs create mode 100644 test.wasm 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 0000000000000000000000000000000000000000..cb1cd8b16d86583ae3a6d6fa8c9c0aa126402697 GIT binary patch literal 85 zcmXBKOA3G>6a~