Compare commits

..

4 Commits

Author SHA1 Message Date
Jesse Brault
86fcbb494b Semantic analysis of generic args and params. 2026-03-16 12:37:10 -05:00
Jesse Brault
3466908a80 Lexing/parsing generic args. 2026-03-16 10:28:53 -05:00
Jesse Brault
b4094bf570 Sketching array-related things. 2026-03-15 19:18:27 -05:00
Jesse Brault
0dce883dbd op_prec example working. 2026-03-15 16:20:55 -05:00
28 changed files with 764 additions and 52 deletions

View File

@ -22,6 +22,10 @@ impl AssignStatement {
} }
} }
pub fn destination(&self) -> &Expression {
&self.destination
}
pub fn gather_declared_names( pub fn gather_declared_names(
&mut self, &mut self,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,

View File

@ -1,22 +1,30 @@
use crate::ast::constructor::Constructor; use crate::ast::constructor::Constructor;
use crate::ast::expression::Expression;
use crate::ast::field::Field; use crate::ast::field::Field;
use crate::ast::fqn_context::FqnContext; use crate::ast::fqn_context::FqnContext;
use crate::ast::fqn_util::fqn_parts_to_string; use crate::ast::fqn_util::fqn_parts_to_string;
use crate::ast::function::Function; use crate::ast::function::Function;
use crate::ast::generic_parameter::GenericParameter;
use crate::ast::statement::Statement;
use crate::diagnostic::{Diagnostic, SecondaryLabel}; use crate::diagnostic::{Diagnostic, SecondaryLabel};
use crate::ir::ir_class::{IrClass, IrField}; use crate::ir::ir_class::{IrClass, IrField};
use crate::ir::ir_function::IrFunction; use crate::ir::ir_function::IrFunction;
use crate::maybe_return_diagnostics;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::Symbol; use crate::symbol::Symbol;
use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::field_symbol::FieldSymbol;
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
use crate::symbol_table::{SymbolInsertError, SymbolTable}; use crate::symbol_table::{SymbolInsertError, SymbolTable};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashSet; use std::collections::{HashMap, HashSet};
use std::process::id;
use std::rc::Rc; use std::rc::Rc;
pub struct Class { pub struct Class {
declared_name: Rc<str>, declared_name: Rc<str>,
declared_name_source_range: SourceRange, declared_name_source_range: SourceRange,
generic_parameters: Vec<GenericParameter>,
constructor: Option<Constructor>, constructor: Option<Constructor>,
fields: Vec<Field>, fields: Vec<Field>,
functions: Vec<Function>, functions: Vec<Function>,
@ -27,6 +35,7 @@ impl Class {
pub fn new( pub fn new(
declared_name: &str, declared_name: &str,
declared_name_source_range: SourceRange, declared_name_source_range: SourceRange,
generic_parameters: Vec<GenericParameter>,
constructor: Option<Constructor>, constructor: Option<Constructor>,
fields: Vec<Field>, fields: Vec<Field>,
functions: Vec<Function>, functions: Vec<Function>,
@ -34,6 +43,7 @@ impl Class {
Class { Class {
declared_name: declared_name.into(), declared_name: declared_name.into(),
declared_name_source_range, declared_name_source_range,
generic_parameters,
constructor, constructor,
fields, fields,
functions, functions,
@ -87,18 +97,51 @@ impl Class {
// 2. push scope // 2. push scope
symbol_table.push_class_scope(&format!("class_scope({})", self.declared_name)); symbol_table.push_class_scope(&format!("class_scope({})", self.declared_name));
// 3. gather fields // 3a. gather generic parameters
let fields_diagnostics: Vec<Diagnostic> = self let mut generic_parameter_symbols: Vec<Rc<RefCell<GenericParameterSymbol>>> = vec![];
.fields let mut generic_parameter_diagnostics: Vec<Diagnostic> = vec![];
.iter_mut() for generic_parameter in &mut self.generic_parameters {
.enumerate() match generic_parameter.gather_declared_names(symbol_table) {
.map(|(field_index, field)| field.gather_declared_names(symbol_table, field_index)) Ok(generic_parameter_symbol) => {
.filter_map(Result::err) generic_parameter_symbols.push(generic_parameter_symbol);
.flatten() }
.collect(); Err(mut d) => {
generic_parameter_diagnostics.append(&mut d);
}
}
}
maybe_return_diagnostics!(generic_parameter_diagnostics);
// save generics to class symbol
self.class_symbol
.as_mut()
.unwrap()
.borrow_mut()
.set_generic_parameters(generic_parameter_symbols);
// 3b. gather fields
let mut field_symbols: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>> = HashMap::new();
let mut fields_diagnostics: Vec<Diagnostic> = vec![];
for (index, field) in self.fields.iter_mut().enumerate() {
match field.gather_declared_names(symbol_table, index) {
Ok(field_symbol) => {
field_symbols.insert(field.declared_name_owned(), field_symbol);
}
Err(mut d) => {
fields_diagnostics.append(&mut d);
}
};
}
if !fields_diagnostics.is_empty() { if !fields_diagnostics.is_empty() {
return Err(fields_diagnostics); return Err(fields_diagnostics);
} else {
self.class_symbol
.as_mut()
.unwrap()
.borrow_mut()
.set_fields(field_symbols);
} }
// 4. gather constructor // 4. gather constructor
@ -142,6 +185,17 @@ impl Class {
} }
pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
let generics_diagnostics: Vec<Diagnostic> = self
.generic_parameters
.iter_mut()
.map(|generic_parameter| {
generic_parameter.check_name_usages(symbol_table, self.class_symbol.as_ref())
})
.filter_map(Result::err)
.flatten()
.collect();
maybe_return_diagnostics!(generics_diagnostics);
self.constructor self.constructor
.as_mut() .as_mut()
.map(|constructor| { .map(|constructor| {
@ -233,9 +287,22 @@ impl Class {
if let Some(constructor) = &self.constructor { if let Some(constructor) = &self.constructor {
for statement in constructor.statements() { for statement in constructor.statements() {
match statement { match statement {
_ => { Statement::Assign(assign_statement) => match assign_statement.destination() {
// no-op for now, because we don't yet have assign statements Expression::Identifier(identifier) => {
} if self
.class_symbol
.as_ref()
.unwrap()
.borrow()
.fields()
.contains_key(identifier.name())
{
initialized_field_names.insert(identifier.name());
}
}
_ => panic!("Found a non L Value assign lhs"),
},
_ => {}
} }
} }
} }

View File

@ -58,7 +58,7 @@ impl Field {
&mut self, &mut self,
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
field_index: usize, field_index: usize,
) -> Result<(), Vec<Diagnostic>> { ) -> Result<Rc<RefCell<FieldSymbol>>, Vec<Diagnostic>> {
// 1. insert field symbol // 1. insert field symbol
let to_insert = FieldSymbol::new( let to_insert = FieldSymbol::new(
&self.declared_name, &self.declared_name,
@ -82,6 +82,7 @@ impl Field {
})?; })?;
// save for later // save for later
let to_return = field_symbol.clone();
self.field_symbol = Some(field_symbol); self.field_symbol = Some(field_symbol);
// set field index on symbol // set field index on symbol
@ -100,7 +101,7 @@ impl Field {
initializer.gather_declared_names(symbol_table)?; initializer.gather_declared_names(symbol_table)?;
} }
Ok(()) Ok(to_return)
} }
pub fn check_name_usages( pub fn check_name_usages(

View File

@ -5,6 +5,7 @@ use crate::ast::parameter::Parameter;
use crate::ast::statement::Statement; use crate::ast::statement::Statement;
use crate::ast::type_use::TypeUse; use crate::ast::type_use::TypeUse;
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::handle_diagnostics;
use crate::ir::ir_function::IrFunction; use crate::ir::ir_function::IrFunction;
use crate::ir::ir_parameter::IrParameter; use crate::ir::ir_parameter::IrParameter;
use crate::ir::ir_parameter_or_variable::IrParameterOrVariable; use crate::ir::ir_parameter_or_variable::IrParameterOrVariable;
@ -109,7 +110,7 @@ impl Function {
Parameter::new( Parameter::new(
"self", "self",
SourceRange::new(0, 0), SourceRange::new(0, 0),
TypeUse::new("Self", SourceRange::new(0, 0)), TypeUse::new("Self", SourceRange::new(0, 0), vec![]),
), ),
); );
} }
@ -238,6 +239,11 @@ impl Function {
let return_type_info = function_symbol.borrow().return_type_info().clone(); let return_type_info = function_symbol.borrow().return_type_info().clone();
let statements_len = self.statements.len(); let statements_len = self.statements.len();
// return type
if let Some(type_use) = &mut self.return_type {
handle_diagnostics!(type_use.type_check(symbol_table), diagnostics);
}
// statements // statements
diagnostics.append( diagnostics.append(
&mut self &mut self

View File

@ -0,0 +1,120 @@
use crate::ast::type_use::TypeUse;
use crate::diagnostic::{Diagnostic, SecondaryLabel};
use crate::error_codes::SYMBOL_ALREADY_DECLARED;
use crate::source_range::SourceRange;
use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
use crate::symbol_table::{SymbolInsertError, SymbolTable};
use crate::{diagnostics_result, handle_diagnostics, maybe_return_diagnostics};
use std::cell::RefCell;
use std::rc::Rc;
pub struct GenericParameter {
declared_name: Rc<str>,
declared_name_source_range: SourceRange,
extends: Vec<TypeUse>,
generic_parameter_symbol: Option<Rc<RefCell<GenericParameterSymbol>>>,
}
impl GenericParameter {
pub fn new(
declared_name: &str,
declared_name_source_range: SourceRange,
extends: Vec<TypeUse>,
) -> Self {
Self {
declared_name: declared_name.into(),
declared_name_source_range,
extends,
generic_parameter_symbol: None,
}
}
pub fn gather_declared_names(
&mut self,
symbol_table: &mut SymbolTable,
) -> Result<Rc<RefCell<GenericParameterSymbol>>, Vec<Diagnostic>> {
// insert symbol
let to_insert =
GenericParameterSymbol::new(&self.declared_name, &self.declared_name_source_range);
let to_return = match symbol_table.insert_generic_parameter_symbol(to_insert) {
Ok(generic_parameter_symbol) => generic_parameter_symbol,
Err(symbol_insert_error) => {
return match symbol_insert_error {
SymbolInsertError::AlreadyDeclared(already_declared) => {
let already_declared_symbol = already_declared.symbol().borrow();
let already_declared_source_range =
already_declared_symbol.declared_name_source_range();
let diagnostic = Diagnostic::new(
&format!("Symbol {} already declared in scope.", self.declared_name),
self.declared_name_source_range.start(),
self.declared_name_source_range.end(),
)
.with_reporter(file!(), line!())
.with_error_code(SYMBOL_ALREADY_DECLARED)
.with_secondary_labels(&[SecondaryLabel::new(
already_declared_source_range.start(),
already_declared_source_range.end(),
Some("Symbol already declared here.".to_string()),
)]);
Err(vec![diagnostic])
}
};
}
};
// save param symbol
self.generic_parameter_symbol = Some(to_return.clone());
let mut diagnostics: Vec<Diagnostic> = vec![];
for type_use in &mut self.extends {
handle_diagnostics!(type_use.gather_declared_names(symbol_table), diagnostics);
}
if diagnostics.is_empty() {
Ok(to_return)
} else {
Err(diagnostics)
}
}
pub fn check_name_usages(
&mut self,
symbol_table: &SymbolTable,
class_context: Option<&Rc<RefCell<ClassSymbol>>>,
) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = vec![];
// check the extends type uses
for type_use in &mut self.extends {
handle_diagnostics!(
type_use.check_name_usages(symbol_table, class_context),
diagnostics
);
}
maybe_return_diagnostics!(diagnostics);
// now that each extends type use has type info, set the type infos on the generic parameter
let extends_type_infos = self
.extends
.iter()
.map(|type_use| type_use.type_info().clone())
.collect::<Vec<_>>();
self.generic_parameter_symbol
.as_mut()
.unwrap()
.borrow_mut()
.set_extends(extends_type_infos);
Ok(())
}
pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = vec![];
// check extends type uses
for type_use in &mut self.extends {
handle_diagnostics!(type_use.type_check(symbol_table), diagnostics);
}
diagnostics_result!(diagnostics)
}
}

View File

@ -13,6 +13,7 @@ pub mod fqn;
pub mod fqn_context; pub mod fqn_context;
pub mod fqn_util; pub mod fqn_util;
pub mod function; pub mod function;
pub mod generic_parameter;
pub mod identifier; pub mod identifier;
pub mod integer_literal; pub mod integer_literal;
pub mod ir_builder; pub mod ir_builder;

View File

@ -81,8 +81,9 @@ impl Parameter {
Ok(()) Ok(())
} }
pub fn type_check(&mut self, _symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
Ok(()) // no-op for now self.type_use.type_check(symbol_table)?;
Ok(())
} }
pub fn parameter_symbol(&self) -> &Rc<RefCell<ParameterSymbol>> { pub fn parameter_symbol(&self) -> &Rc<RefCell<ParameterSymbol>> {

View File

@ -1,23 +1,31 @@
use crate::diagnostic::Diagnostic; use crate::diagnostic::Diagnostic;
use crate::error_codes::INCORRECT_GENERIC_ARGUMENTS;
use crate::source_range::SourceRange; use crate::source_range::SourceRange;
use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::type_info::TypeInfo; use crate::type_info::TypeInfo;
use crate::{diagnostics_result, handle_diagnostics, maybe_return_diagnostics};
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
pub struct TypeUse { pub struct TypeUse {
declared_name: String, declared_name: String,
declared_name_source_range: SourceRange, declared_name_source_range: SourceRange,
generic_arguments: Vec<TypeUse>,
scope_id: Option<usize>, scope_id: Option<usize>,
type_info: Option<TypeInfo>, type_info: Option<TypeInfo>,
} }
impl TypeUse { impl TypeUse {
pub fn new(declared_name: &str, declared_name_source_range: SourceRange) -> Self { pub fn new(
declared_name: &str,
declared_name_source_range: SourceRange,
generic_arguments: Vec<TypeUse>,
) -> Self {
Self { Self {
declared_name: declared_name.into(), declared_name: declared_name.into(),
declared_name_source_range, declared_name_source_range,
generic_arguments,
scope_id: None, scope_id: None,
type_info: None, type_info: None,
} }
@ -32,7 +40,14 @@ impl TypeUse {
symbol_table: &mut SymbolTable, symbol_table: &mut SymbolTable,
) -> Result<(), Vec<Diagnostic>> { ) -> Result<(), Vec<Diagnostic>> {
self.scope_id = Some(symbol_table.current_scope_id()); self.scope_id = Some(symbol_table.current_scope_id());
Ok(()) let mut inner_diagnostics: Vec<Diagnostic> = vec![];
for generic_argument in &mut self.generic_arguments {
handle_diagnostics!(
generic_argument.gather_declared_names(symbol_table),
inner_diagnostics
);
}
diagnostics_result!(inner_diagnostics)
} }
pub fn check_name_usages( pub fn check_name_usages(
@ -40,6 +55,8 @@ impl TypeUse {
symbol_table: &SymbolTable, symbol_table: &SymbolTable,
class_context: Option<&Rc<RefCell<ClassSymbol>>>, class_context: Option<&Rc<RefCell<ClassSymbol>>>,
) -> Result<(), Vec<Diagnostic>> { ) -> Result<(), Vec<Diagnostic>> {
let mut diagnostics: Vec<Diagnostic> = vec![];
if let Some(type_info) = TypeInfo::from_declared_name( if let Some(type_info) = TypeInfo::from_declared_name(
&self.declared_name, &self.declared_name,
self.scope_id.unwrap(), self.scope_id.unwrap(),
@ -47,24 +64,114 @@ impl TypeUse {
class_context, class_context,
) { ) {
self.type_info = Some(type_info); self.type_info = Some(type_info);
Ok(())
} else { } else {
Err(vec![ let diagnostic = Diagnostic::new(
Diagnostic::new( &format!("Unable to resolve symbol {}", self.declared_name),
&format!("Unable to resolve symbol {}", self.declared_name), self.declared_name_source_range.start(),
self.declared_name_source_range.start(), self.declared_name_source_range.end(),
self.declared_name_source_range.end(), )
) .with_reporter(file!(), line!());
.with_reporter(file!(), line!()), diagnostics.push(diagnostic);
])
} }
for generic_argument in &mut self.generic_arguments {
handle_diagnostics!(
generic_argument.check_name_usages(symbol_table, class_context),
diagnostics
);
}
diagnostics_result!(diagnostics)
} }
pub fn type_check(&mut self, _symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> { pub fn type_check(&mut self, _symbol_table: &SymbolTable) -> Result<(), Vec<Diagnostic>> {
Ok(()) // no-op, for now let mut diagnostics: Vec<Diagnostic> = vec![];
match self.type_info() {
TypeInfo::ClassInstance(class_symbol) => {
// check number of params/args match
let borrowed_class_symbol = class_symbol.borrow();
let generic_parameters = borrowed_class_symbol.generic_parameters();
if generic_parameters.len() != self.generic_arguments.len() {
let diagnostic = Diagnostic::new(
&format!(
"Expected {} generic arguments; found {}.",
generic_parameters.len(),
self.generic_arguments.len()
),
self.declared_name_source_range.start(),
self.declared_name_source_range.end(),
)
.with_reporter(file!(), line!())
.with_error_code(INCORRECT_GENERIC_ARGUMENTS);
diagnostics.push(diagnostic);
}
maybe_return_diagnostics!(diagnostics);
// check that each arg is assignable to the param's extends
for i in 0..self.generic_arguments.len() {
let generic_parameter_symbol = generic_parameters[i].borrow();
if generic_parameter_symbol.extends().len() > 0 {
unimplemented!("Generic extends not implemented yet.")
}
}
}
_ => {
// cannot extend a non-class type (except for Any)
if self.generic_arguments.len() > 0 {
let diagnostic = Diagnostic::new(
&format!(
"Type {} does not accept generic arguments.",
self.type_info()
),
self.declared_name_source_range.start(),
self.declared_name_source_range.end(),
)
.with_reporter(file!(), line!())
.with_error_code(INCORRECT_GENERIC_ARGUMENTS);
diagnostics.push(diagnostic);
}
}
}
// recurse on generic arguments
for generic_argument in &mut self.generic_arguments {
handle_diagnostics!(generic_argument.type_check(_symbol_table), diagnostics);
}
diagnostics_result!(diagnostics)
} }
pub fn type_info(&self) -> &TypeInfo { pub fn type_info(&self) -> &TypeInfo {
self.type_info.as_ref().unwrap() self.type_info.as_ref().unwrap()
} }
} }
#[cfg(test)]
mod tests {
use crate::diagnostic::Diagnostic;
use crate::parser::parse_compilation_unit;
use crate::symbol_table::SymbolTable;
#[test]
fn type_check_generics() -> Result<(), Vec<Diagnostic>> {
let mut compilation_unit = parse_compilation_unit(
"
class Foo<T>
ctor(t: T) end
end
fn useFoo(foo: Foo<String>) end
",
)?;
let mut symbol_table = SymbolTable::new();
compilation_unit.gather_declared_names(&mut symbol_table)?;
compilation_unit.check_name_usages(&mut symbol_table)?;
compilation_unit.type_check(&mut symbol_table)?;
Ok(())
}
}

View File

@ -1,6 +1,9 @@
pub type ErrorCode = usize; pub type ErrorCode = usize;
pub const SYMBOL_ALREADY_DECLARED: ErrorCode = 14;
pub const BINARY_INCOMPATIBLE_TYPES: ErrorCode = 15; pub const BINARY_INCOMPATIBLE_TYPES: ErrorCode = 15;
pub const ASSIGN_MISMATCHED_TYPES: ErrorCode = 16; pub const ASSIGN_MISMATCHED_TYPES: ErrorCode = 16;
pub const ASSIGN_NO_L_VALUE: ErrorCode = 17; pub const ASSIGN_NO_L_VALUE: ErrorCode = 17;
pub const ASSIGN_LHS_IMMUTABLE: ErrorCode = 18; pub const ASSIGN_LHS_IMMUTABLE: ErrorCode = 18;
pub const INCORRECT_GENERIC_ARGUMENTS: ErrorCode = 19;
pub const GENERIC_ARGUMENT_TYPE_MISMATCH: ErrorCode = 20;

View File

@ -54,6 +54,7 @@ impl IrField {
TypeInfo::ClassInstance(class_symbol) => VmTypeInfo::ClassInstance( TypeInfo::ClassInstance(class_symbol) => VmTypeInfo::ClassInstance(
fqn_parts_to_string(class_symbol.borrow().fqn_parts()).into(), fqn_parts_to_string(class_symbol.borrow().fqn_parts()).into(),
), ),
TypeInfo::GenericType(_) => VmTypeInfo::Any,
_ => panic!(), _ => panic!(),
}, },
) )

View File

@ -54,10 +54,6 @@ impl<'a> Lexer<'a> {
Token::new(self.position, self.position + 1, TokenKind::Modulo) Token::new(self.position, self.position + 1, TokenKind::Modulo)
} else if chunk.starts_with("+") { } else if chunk.starts_with("+") {
Token::new(self.position, self.position + 1, TokenKind::Plus) Token::new(self.position, self.position + 1, TokenKind::Plus)
} else if chunk.starts_with("<<") {
Token::new(self.position, self.position + 2, TokenKind::LeftShift)
} else if chunk.starts_with(">>") {
Token::new(self.position, self.position + 2, TokenKind::RightShift)
} else if chunk.starts_with("&") { } else if chunk.starts_with("&") {
Token::new(self.position, self.position + 1, TokenKind::Ampersand) Token::new(self.position, self.position + 1, TokenKind::Ampersand)
} else if chunk.starts_with("^") { } else if chunk.starts_with("^") {
@ -72,6 +68,14 @@ impl<'a> Lexer<'a> {
Token::new(self.position, self.position + 1, TokenKind::Colon) Token::new(self.position, self.position + 1, TokenKind::Colon)
} else if chunk.starts_with(".") { } else if chunk.starts_with(".") {
Token::new(self.position, self.position + 1, TokenKind::Dot) Token::new(self.position, self.position + 1, TokenKind::Dot)
} else if chunk.starts_with("[") {
Token::new(self.position, self.position + 1, TokenKind::LeftSquare)
} else if chunk.starts_with("]") {
Token::new(self.position, self.position + 1, TokenKind::RightSquare)
} else if chunk.starts_with("<") {
Token::new(self.position, self.position + 1, TokenKind::Lt)
} else if chunk.starts_with(">") {
Token::new(self.position, self.position + 1, TokenKind::Gt)
} else { } else {
// more than one char token // more than one char token
if chunk.starts_with(|c: char| c.is_ascii_digit()) { if chunk.starts_with(|c: char| c.is_ascii_digit()) {

View File

@ -10,6 +10,7 @@ use crate::ast::expression_statement::ExpressionStatement;
use crate::ast::extern_function::ExternFunction; use crate::ast::extern_function::ExternFunction;
use crate::ast::field::Field; use crate::ast::field::Field;
use crate::ast::function::Function; use crate::ast::function::Function;
use crate::ast::generic_parameter::GenericParameter;
use crate::ast::identifier::Identifier; use crate::ast::identifier::Identifier;
use crate::ast::integer_literal::IntegerLiteral; use crate::ast::integer_literal::IntegerLiteral;
use crate::ast::let_statement::LetStatement; use crate::ast::let_statement::LetStatement;
@ -56,6 +57,12 @@ macro_rules! matches_statement_first {
}; };
} }
macro_rules! matches_type_use_first {
( $token_kind: expr ) => {
matches!($token_kind, TokenKind::LeftSquare | TokenKind::Identifier)
};
}
struct Parser<'a> { struct Parser<'a> {
input: &'a str, input: &'a str,
lexer: Lexer<'a>, lexer: Lexer<'a>,
@ -163,6 +170,26 @@ impl<'a> Parser<'a> {
} }
} }
fn expect_position_advance(
&mut self,
token_kind: TokenKind,
start_position: usize,
) -> Result<Token, Vec<Diagnostic>> {
let matched = self.expect_advance(token_kind)?;
if matched.start() == start_position {
Ok(matched)
} else {
Err(vec![
Diagnostic::new(
&format!("Expected {:?} but found {:?}", token_kind, matched.kind()),
matched.start(),
matched.end(),
)
.with_reporter(file!(), line!()),
])
}
}
fn peek_current(&self, token_kind: TokenKind) -> bool { fn peek_current(&self, token_kind: TokenKind) -> bool {
match &self.current { match &self.current {
None => panic!("Unexpected end of input."), None => panic!("Unexpected end of input."),
@ -387,6 +414,7 @@ impl<'a> Parser<'a> {
fn class(&mut self) -> Result<Class, Vec<Diagnostic>> { fn class(&mut self) -> Result<Class, Vec<Diagnostic>> {
self.expect_advance(TokenKind::Class)?; self.expect_advance(TokenKind::Class)?;
let identifier_token = self.expect_advance(TokenKind::Identifier)?; let identifier_token = self.expect_advance(TokenKind::Identifier)?;
let mut generic_parameters: Vec<GenericParameter> = vec![];
let mut fields = vec![]; let mut fields = vec![];
let mut functions = vec![]; let mut functions = vec![];
let mut maybe_constructor: Option<Constructor> = None; let mut maybe_constructor: Option<Constructor> = None;
@ -417,6 +445,14 @@ impl<'a> Parser<'a> {
} }
Err(mut ctor_diagnostics) => diagnostics.append(&mut ctor_diagnostics), Err(mut ctor_diagnostics) => diagnostics.append(&mut ctor_diagnostics),
}, },
TokenKind::Lt => match self.generic_parameters() {
Ok(parsed_params) => {
generic_parameters = parsed_params;
}
Err(mut generic_parameters_diagnostics) => {
diagnostics.append(&mut generic_parameters_diagnostics);
}
},
_ => unreachable!(), _ => unreachable!(),
} }
} }
@ -427,6 +463,7 @@ impl<'a> Parser<'a> {
Ok(Class::new( Ok(Class::new(
self.token_text(&identifier_token), self.token_text(&identifier_token),
SourceRange::new(identifier_token.start(), identifier_token.end()), SourceRange::new(identifier_token.start(), identifier_token.end()),
generic_parameters,
maybe_constructor, maybe_constructor,
fields, fields,
functions, functions,
@ -477,10 +514,95 @@ impl<'a> Parser<'a> {
} }
fn type_use(&mut self) -> Result<TypeUse, Vec<Diagnostic>> { fn type_use(&mut self) -> Result<TypeUse, Vec<Diagnostic>> {
let identifier_token = self.expect_advance(TokenKind::Identifier)?; if self.current.is_some() {
Ok(TypeUse::new( let current = self.get_current();
self.token_text(&identifier_token), return match current.kind() {
SourceRange::new(identifier_token.start(), identifier_token.end()), TokenKind::LeftSquare => {
self.advance(); // [
let inner_type_use = self.type_use()?;
self.expect_advance(TokenKind::RightSquare)?;
todo!()
}
TokenKind::Identifier => {
let identifier_token = self.expect_advance(TokenKind::Identifier)?;
let generic_arguments =
if self.current.is_some() && self.peek_current(TokenKind::Lt) {
self.advance(); // <
let generic_arguments = self.generic_arguments_list()?;
self.expect_advance(TokenKind::Gt)?; // >
generic_arguments
} else {
vec![]
};
Ok(TypeUse::new(
self.token_text(&identifier_token),
SourceRange::new(identifier_token.start(), identifier_token.end()),
generic_arguments,
))
}
_ => Err(vec![Diagnostic::new(
&format!(
"Expected LeftSquare or Identifier; found: {:?}",
current.kind()
),
current.start(),
current.end(),
)]),
};
}
Err(vec![Diagnostic::new(
"Expected LeftSquare or Identifier; found end of input.",
self.input.len(),
self.input.len(),
)])
}
fn generic_arguments_list(&mut self) -> Result<Vec<TypeUse>, Vec<Diagnostic>> {
let mut generic_arguments: Vec<TypeUse> = vec![];
while self.current.is_some() && matches_type_use_first!(self.get_current().kind()) {
generic_arguments.push(self.type_use()?);
if self.current.is_some() && self.peek_current(TokenKind::Comma) {
self.advance(); // comma
} else {
break;
}
}
Ok(generic_arguments)
}
fn generic_parameters(&mut self) -> Result<Vec<GenericParameter>, Vec<Diagnostic>> {
self.expect_advance(TokenKind::Lt)?;
let mut parameters: Vec<GenericParameter> = vec![];
while self.current.is_some() && self.peek_current(TokenKind::Identifier) {
parameters.push(self.generic_parameter()?);
if self.current.is_some() && self.peek_current(TokenKind::Plus) {
self.advance(); // +
} else {
break;
}
}
self.expect_advance(TokenKind::Gt)?;
Ok(parameters)
}
fn generic_parameter(&mut self) -> Result<GenericParameter, Vec<Diagnostic>> {
let identifier = self.expect_advance(TokenKind::Identifier)?;
let mut extends_list: Vec<TypeUse> = vec![];
if self.current.is_some() && self.peek_current(TokenKind::Colon) {
self.advance(); // :
while self.current.is_some() && matches_type_use_first!(self.get_current().kind()) {
extends_list.push(self.type_use()?);
if self.current.is_some() && self.peek_current(TokenKind::Comma) {
self.advance(); // ,
} else {
break;
}
}
}
Ok(GenericParameter::new(
self.token_text(&identifier),
SourceRange::new(identifier.start(), identifier.end()),
extends_list,
)) ))
} }
@ -721,8 +843,10 @@ impl<'a> Parser<'a> {
while self.current.is_some() { while self.current.is_some() {
let current = self.get_current(); let current = self.get_current();
match current.kind() { match current.kind() {
TokenKind::LeftShift => { TokenKind::Lt => {
self.advance(); // left shift let second_lt_start = current.start() + 1;
self.advance(); // first <
self.expect_position_advance(TokenKind::Lt, second_lt_start)?; // second <
let rhs = self.additive_expression()?; let rhs = self.additive_expression()?;
let source_range = let source_range =
SourceRange::new(result.source_range().start(), rhs.source_range().end()); SourceRange::new(result.source_range().start(), rhs.source_range().end());
@ -733,8 +857,10 @@ impl<'a> Parser<'a> {
source_range, source_range,
)); ));
} }
TokenKind::RightShift => { TokenKind::Gt => {
self.advance(); // right shift let second_gt_start = current.start() + 1;
self.advance(); // first >
self.expect_position_advance(TokenKind::Gt, second_gt_start)?; // second gt
let rhs = self.additive_expression()?; let rhs = self.additive_expression()?;
let source_range = let source_range =
SourceRange::new(result.source_range().start(), rhs.source_range().end()); SourceRange::new(result.source_range().start(), rhs.source_range().end());
@ -1144,6 +1270,21 @@ mod smoke_tests {
", ",
) )
} }
#[test]
fn array_generic_arg() {
smoke_test("fn main(args: Array<String>) end");
}
#[test]
fn nested_generic_args() {
smoke_test("fn main(foo: Array<Bar<Foo>>) end");
}
#[test]
fn class_with_generic_param() {
smoke_test("class Foo<T> end");
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -2,6 +2,7 @@ use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::constructor_symbol::ConstructorSymbol; use crate::symbol::constructor_symbol::ConstructorSymbol;
use crate::symbol::field_symbol::FieldSymbol; use crate::symbol::field_symbol::FieldSymbol;
use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol::function_symbol::FunctionSymbol;
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
@ -9,6 +10,7 @@ use std::rc::Rc;
pub struct ClassScope { pub struct ClassScope {
debug_name: String, debug_name: String,
parent_id: usize, parent_id: usize,
generic_parameter_symbols: HashMap<Rc<str>, Rc<RefCell<GenericParameterSymbol>>>,
class_symbols: HashMap<Rc<str>, Rc<RefCell<ClassSymbol>>>, class_symbols: HashMap<Rc<str>, Rc<RefCell<ClassSymbol>>>,
field_symbols: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>>, field_symbols: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>>,
function_symbols: HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>>, function_symbols: HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>>,
@ -20,6 +22,7 @@ impl ClassScope {
Self { Self {
debug_name: debug_name.into(), debug_name: debug_name.into(),
parent_id, parent_id,
generic_parameter_symbols: HashMap::new(),
class_symbols: HashMap::new(), class_symbols: HashMap::new(),
field_symbols: HashMap::new(), field_symbols: HashMap::new(),
function_symbols: HashMap::new(), function_symbols: HashMap::new(),
@ -27,6 +30,18 @@ impl ClassScope {
} }
} }
pub fn generic_parameter_symbols(
&self,
) -> &HashMap<Rc<str>, Rc<RefCell<GenericParameterSymbol>>> {
&self.generic_parameter_symbols
}
pub fn generic_parameter_symbols_mut(
&mut self,
) -> &mut HashMap<Rc<str>, Rc<RefCell<GenericParameterSymbol>>> {
&mut self.generic_parameter_symbols
}
pub fn class_symbols(&self) -> &HashMap<Rc<str>, Rc<RefCell<ClassSymbol>>> { pub fn class_symbols(&self) -> &HashMap<Rc<str>, Rc<RefCell<ClassSymbol>>> {
&self.class_symbols &self.class_symbols
} }

View File

@ -3,6 +3,7 @@ use crate::symbol::Symbol;
use crate::symbol::constructor_symbol::ConstructorSymbol; use crate::symbol::constructor_symbol::ConstructorSymbol;
use crate::symbol::field_symbol::FieldSymbol; use crate::symbol::field_symbol::FieldSymbol;
use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol::function_symbol::FunctionSymbol;
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::Rc; use std::rc::Rc;
@ -12,6 +13,7 @@ pub struct ClassSymbol {
declared_name_source_range: SourceRange, declared_name_source_range: SourceRange,
fqn_parts: Vec<Rc<str>>, fqn_parts: Vec<Rc<str>>,
is_extern: bool, is_extern: bool,
generic_parameters: Vec<Rc<RefCell<GenericParameterSymbol>>>,
constructor_symbol: Option<Rc<RefCell<ConstructorSymbol>>>, constructor_symbol: Option<Rc<RefCell<ConstructorSymbol>>>,
fields: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>>, fields: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>>,
functions: HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>>, functions: HashMap<Rc<str>, Rc<RefCell<FunctionSymbol>>>,
@ -29,6 +31,7 @@ impl ClassSymbol {
declared_name_source_range, declared_name_source_range,
fqn_parts, fqn_parts,
is_extern, is_extern,
generic_parameters: vec![],
constructor_symbol: None, constructor_symbol: None,
fields: HashMap::new(), fields: HashMap::new(),
functions: HashMap::new(), functions: HashMap::new(),
@ -39,6 +42,17 @@ impl ClassSymbol {
&self.fqn_parts &self.fqn_parts
} }
pub fn set_generic_parameters(
&mut self,
generic_parameters: Vec<Rc<RefCell<GenericParameterSymbol>>>,
) {
self.generic_parameters = generic_parameters;
}
pub fn generic_parameters(&self) -> &[Rc<RefCell<GenericParameterSymbol>>] {
&self.generic_parameters
}
pub fn set_constructor_symbol( pub fn set_constructor_symbol(
&mut self, &mut self,
constructor_symbol: Option<Rc<RefCell<ConstructorSymbol>>>, constructor_symbol: Option<Rc<RefCell<ConstructorSymbol>>>,
@ -46,6 +60,10 @@ impl ClassSymbol {
self.constructor_symbol = constructor_symbol; self.constructor_symbol = constructor_symbol;
} }
pub fn set_fields(&mut self, fields: HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>>) {
self.fields = fields;
}
pub fn fields(&self) -> &HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>> { pub fn fields(&self) -> &HashMap<Rc<str>, Rc<RefCell<FieldSymbol>>> {
&self.fields &self.fields
} }

View File

@ -0,0 +1,42 @@
use crate::source_range::SourceRange;
use crate::symbol::Symbol;
use crate::type_info::TypeInfo;
use std::rc::Rc;
pub struct GenericParameterSymbol {
declared_name: Rc<str>,
declared_name_source_range: SourceRange,
extends: Option<Vec<TypeInfo>>,
}
impl GenericParameterSymbol {
pub fn new(declared_name: &Rc<str>, declared_name_source_range: &SourceRange) -> Self {
Self {
declared_name: declared_name.clone(),
declared_name_source_range: declared_name_source_range.clone(),
extends: None,
}
}
pub fn set_extends(&mut self, extends: Vec<TypeInfo>) {
self.extends = Some(extends);
}
pub fn extends(&self) -> &[TypeInfo] {
self.extends.as_ref().unwrap()
}
}
impl Symbol for GenericParameterSymbol {
fn declared_name(&self) -> &str {
&self.declared_name
}
fn declared_name_owned(&self) -> Rc<str> {
self.declared_name.clone()
}
fn declared_name_source_range(&self) -> &SourceRange {
&self.declared_name_source_range
}
}

View File

@ -7,6 +7,7 @@ pub mod constructor_symbol;
pub mod expressible_symbol; pub mod expressible_symbol;
pub mod field_symbol; pub mod field_symbol;
pub mod function_symbol; pub mod function_symbol;
pub mod generic_parameter_symbol;
pub mod parameter_symbol; pub mod parameter_symbol;
pub mod type_symbol; pub mod type_symbol;
pub mod variable_symbol; pub mod variable_symbol;

View File

@ -1,7 +1,9 @@
use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
pub enum TypeSymbol { pub enum TypeSymbol {
Class(Rc<RefCell<ClassSymbol>>), Class(Rc<RefCell<ClassSymbol>>),
GenericParameter(Rc<RefCell<GenericParameterSymbol>>),
} }

View File

@ -143,11 +143,19 @@ fn find_type_symbol_in_module(module_scope: &ModuleScope, name: &str) -> Option<
.map(|symbol| TypeSymbol::Class(symbol.clone())) .map(|symbol| TypeSymbol::Class(symbol.clone()))
} }
fn find_type_symbol_in_class(class_scope: &ClassScope, name: &str) -> Option<TypeSymbol> { pub fn find_type_symbol_in_class(class_scope: &ClassScope, name: &str) -> Option<TypeSymbol> {
class_scope class_scope
.class_symbols() .class_symbols()
.get(name) .get(name)
.map(|symbol| TypeSymbol::Class(symbol.clone())) .map(|symbol| TypeSymbol::Class(symbol.clone()))
.or_else(|| {
class_scope
.generic_parameter_symbols()
.get(name)
.map(|generic_parameter_symbol| {
TypeSymbol::GenericParameter(generic_parameter_symbol.clone())
})
})
} }
pub fn find_type_symbol(scope: &Scope, name: &str) -> Option<TypeSymbol> { pub fn find_type_symbol(scope: &Scope, name: &str) -> Option<TypeSymbol> {

View File

@ -11,12 +11,13 @@ use crate::symbol::constructor_symbol::ConstructorSymbol;
use crate::symbol::expressible_symbol::ExpressibleSymbol; use crate::symbol::expressible_symbol::ExpressibleSymbol;
use crate::symbol::field_symbol::FieldSymbol; use crate::symbol::field_symbol::FieldSymbol;
use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol::function_symbol::FunctionSymbol;
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
use crate::symbol::parameter_symbol::ParameterSymbol; use crate::symbol::parameter_symbol::ParameterSymbol;
use crate::symbol::type_symbol::TypeSymbol; use crate::symbol::type_symbol::TypeSymbol;
use crate::symbol::variable_symbol::VariableSymbol; use crate::symbol::variable_symbol::VariableSymbol;
use crate::symbol_table::helpers::{ use crate::symbol_table::helpers::{
find_expressible_symbol, find_in_block_by_name, find_in_class_by_name, find_expressible_symbol, find_in_block_by_name, find_in_class_by_name,
find_in_function_by_name, find_in_module_by_name, find_type_symbol, find_in_function_by_name, find_in_module_by_name, find_type_symbol, find_type_symbol_in_class,
}; };
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
@ -124,6 +125,48 @@ impl SymbolTable {
Ok(to_return) Ok(to_return)
} }
pub fn insert_generic_parameter_symbol(
&mut self,
generic_parameter_symbol: GenericParameterSymbol,
) -> Result<Rc<RefCell<GenericParameterSymbol>>, SymbolInsertError> {
let maybe_already_inserted = match self.current_scope() {
Scope::Class(class_scope) => {
find_type_symbol_in_class(class_scope, generic_parameter_symbol.declared_name())
}
_ => panic!("Attempt to insert GenericParameterSymbol in incompatible scope"),
};
if let Some(already_inserted) = maybe_already_inserted {
match already_inserted {
TypeSymbol::Class(class_symbol) => {
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
class_symbol as Rc<RefCell<dyn Symbol>>,
)));
}
TypeSymbol::GenericParameter(generic_parameter_symbol) => {
return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new(
generic_parameter_symbol as Rc<RefCell<dyn Symbol>>,
)));
}
}
}
let name = generic_parameter_symbol.declared_name_owned();
let as_rc = Rc::new(RefCell::new(generic_parameter_symbol));
let to_return = as_rc.clone();
match self.current_scope_mut() {
Scope::Class(class_scope) => {
class_scope
.generic_parameter_symbols_mut()
.insert(name, as_rc);
}
_ => unreachable!(),
}
Ok(to_return)
}
pub fn insert_constructor_symbol( pub fn insert_constructor_symbol(
&mut self, &mut self,
constructor_symbol: ConstructorSymbol, constructor_symbol: ConstructorSymbol,

View File

@ -45,8 +45,6 @@ pub enum TokenKind {
Star, Star,
Slash, Slash,
Modulo, Modulo,
LeftShift,
RightShift,
Ampersand, Ampersand,
Caret, Caret,
Bar, Bar,
@ -56,4 +54,8 @@ pub enum TokenKind {
Public, Public,
Mut, Mut,
Ctor, Ctor,
LeftSquare,
RightSquare,
Lt,
Gt,
} }

View File

@ -1,6 +1,7 @@
use crate::symbol::Symbol; use crate::symbol::Symbol;
use crate::symbol::class_symbol::ClassSymbol; use crate::symbol::class_symbol::ClassSymbol;
use crate::symbol::function_symbol::FunctionSymbol; use crate::symbol::function_symbol::FunctionSymbol;
use crate::symbol::generic_parameter_symbol::GenericParameterSymbol;
use crate::symbol::type_symbol::TypeSymbol; use crate::symbol::type_symbol::TypeSymbol;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use std::cell::RefCell; use std::cell::RefCell;
@ -15,6 +16,7 @@ pub enum TypeInfo {
String, String,
Function(Rc<RefCell<FunctionSymbol>>), Function(Rc<RefCell<FunctionSymbol>>),
ClassInstance(Rc<RefCell<ClassSymbol>>), ClassInstance(Rc<RefCell<ClassSymbol>>),
GenericType(Rc<RefCell<GenericParameterSymbol>>),
Void, Void,
} }
@ -35,6 +37,9 @@ impl Display for TypeInfo {
TypeInfo::ClassInstance(class_symbol) => { TypeInfo::ClassInstance(class_symbol) => {
write!(f, "{}", class_symbol.borrow().declared_name()) write!(f, "{}", class_symbol.borrow().declared_name())
} }
TypeInfo::GenericType(generic_parameter_symbol) => {
write!(f, "{}", generic_parameter_symbol.borrow().declared_name())
}
TypeInfo::Void => write!(f, "Void"), TypeInfo::Void => write!(f, "Void"),
} }
} }
@ -66,6 +71,9 @@ impl TypeInfo {
None => None, None => None,
Some(type_symbol) => match type_symbol { Some(type_symbol) => match type_symbol {
TypeSymbol::Class(class_symbol) => Some(TypeInfo::ClassInstance(class_symbol)), TypeSymbol::Class(class_symbol) => Some(TypeInfo::ClassInstance(class_symbol)),
TypeSymbol::GenericParameter(generic_parameter_symbol) => {
Some(TypeInfo::GenericType(generic_parameter_symbol))
}
}, },
}, },
} }
@ -95,6 +103,14 @@ impl TypeInfo {
_ => false, _ => false,
} }
} }
TypeInfo::GenericType(generic_parameter_symbol) => {
if generic_parameter_symbol.borrow().extends().len() > 0 {
unimplemented!(
"Assigning to generic parameter type with extends type uses not yet supported."
);
}
true
}
TypeInfo::Void => { TypeInfo::Void => {
matches!(other, TypeInfo::Void) matches!(other, TypeInfo::Void)
} }

44
dvm-lib/src/vm/array.rs Normal file
View File

@ -0,0 +1,44 @@
use std::alloc::{Layout, alloc};
#[derive(Debug)]
#[repr(C)]
pub struct Array<T> {
header: ArrayHeader,
data: [T],
}
#[derive(Debug)]
pub struct ArrayHeader {
length: usize,
}
pub fn get_array<T: Clone>(length: usize, init: T) -> Box<Array<T>> {
let (layout, data_base) = Layout::array::<T>(length)
.and_then(|data_layout| Layout::new::<ArrayHeader>().extend(data_layout))
.unwrap();
let ptr = unsafe { alloc(layout) };
if ptr.is_null() {
panic!("failed to allocate memory");
}
unsafe {
ptr.cast::<ArrayHeader>().write(ArrayHeader { length });
let data_ptr = ptr.add(data_base).cast::<T>();
for i in 0..length {
data_ptr.add(i).write(init.clone());
}
Box::from_raw(std::ptr::slice_from_raw_parts(ptr as *mut T, length) as *mut Array<T>)
}
}
#[cfg(test)]
mod tests {
use crate::vm::array::get_array;
#[test]
fn int_array() {
let int_array = get_array::<i32>(5, 0);
assert_eq!(int_array.data.len(), 5);
assert_eq!(int_array.header.length, 5);
}
}

View File

@ -17,6 +17,7 @@ use std::collections::HashMap;
use std::ptr; use std::ptr;
use std::rc::Rc; use std::rc::Rc;
pub mod array;
pub mod class; pub mod class;
pub mod constant; pub mod constant;
pub mod function; pub mod function;

View File

@ -8,6 +8,7 @@ pub enum TypeInfo {
Int, Int,
Double, Double,
String, String,
Any,
} }
impl TypeInfo { impl TypeInfo {
@ -36,6 +37,7 @@ impl TypeInfo {
TypeInfo::String => { TypeInfo::String => {
matches!(value, Value::String(_)) matches!(value, Value::String(_))
} }
TypeInfo::Any => true,
} }
} }
} }

View File

@ -290,6 +290,31 @@ mod e2e_tests {
assert_eq!(o.fields().len(), 1); assert_eq!(o.fields().len(), 1);
assert_eq!(o.fields()[0].unwrap_int(), 42); assert_eq!(o.fields()[0].unwrap_int(), 42);
} }
#[test]
fn see_what_happens() {
let context = prepare_context(
"
class Foo<T>
mut t: T
ctor(_t: T)
t = _t
end
end
fn main() -> Foo<Int>
Foo(42)
end
",
);
let result = get_result(&context, "main", &vec![]);
assert!(result.is_some());
let value = result.unwrap();
assert!(matches!(value, Value::Object(_)));
let o = value.unwrap_object().borrow();
assert_eq!(o.fields().len(), 1);
assert_eq!(o.fields()[0].unwrap_int(), 42);
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -1,3 +1,5 @@
extern fn println(message: Any) -> Void
fn main() fn main()
let n = 1 + 2 * 3 + 4 let n = 1 + 2 * 3 + 4
println(n) println(n)

View File

@ -1,5 +0,0 @@
fn main() -> Void
$0 = 2 * 3
$1 = 1 + $0
$2 = $1 + 4
call std::core::println($2)

View File

@ -0,0 +1,40 @@
fn main(args: [String])
println(args[0])
end
int Index<T, I>
op [](i: I) -> T
end
int Slice<T> : Index<T, ISize>, Iterable<T>
length: ISize
def fn iter() -> Iterator<T>
let mut i = 0
{
if i < length then
i++
Some(self[i - 1])
else
None
end
}
end
end
int Iterator<T>
fn next() -> Option<T>
end
int Iterable<T>
fn iter() -> Iterator<T>
end
extern class Array<T> : Index<T, ISize>, Slice<T>
extern length: ISize
extern op [](i: ISize) -> T
end
int List<T> : Index<T, ISize>, Slice<T>
end