added structs

This commit is contained in:
Logan 2024-10-22 11:27:39 -05:00
parent 0edbc46aa0
commit 47c6ccfa6b
17 changed files with 1171 additions and 349 deletions

97
Cargo.lock generated
View file

@ -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",
]

View file

@ -4,3 +4,4 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
wat = "1.219"

View file

@ -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
View 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>

View file

@ -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)
} }
} }

View file

@ -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();
}
} }

View file

@ -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(())
} }

View file

@ -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)> {

View file

@ -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;

View file

@ -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
View file

@ -0,0 +1,5 @@
mod primitives;
mod types;
pub use primitives::*;
pub use types::*;

172
src/semantic/primitives.rs Normal file
View 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
View 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)
}

View file

@ -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),

View file

@ -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(())
} }
} }
*/

View file

@ -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;
}
}
}
}

BIN
test.wasm Normal file

Binary file not shown.