diff --git a/schala-lang/src/ast/mod.rs b/schala-lang/src/ast/mod.rs index 2a9c8ac..2700604 100644 --- a/schala-lang/src/ast/mod.rs +++ b/schala-lang/src/ast/mod.rs @@ -18,6 +18,7 @@ use crate::{ derivative::Derivative, identifier::{define_id_kind, Id}, parsing::Location, + util::delim_wrapped, }; define_id_kind!(ASTItem); @@ -40,12 +41,12 @@ impl fmt::Display for AST { #[derive(Derivative, Debug, Clone)] #[derivative(PartialEq)] -pub struct Statement { +pub struct Statement { #[derivative(PartialEq = "ignore")] pub id: ItemId, #[derivative(PartialEq = "ignore")] pub location: Location, - pub kind: StatementKind, + pub kind: K, } #[derive(Debug, PartialEq, Clone)] @@ -65,23 +66,23 @@ pub enum FlowControl { #[derive(Debug, Clone, PartialEq, Default)] pub struct Block { - pub statements: Vec, + pub statements: Vec>, } -impl From> for Block { - fn from(statements: Vec) -> Self { +impl From>> for Block { + fn from(statements: Vec>) -> Self { Self { statements } } } -impl From for Block { - fn from(statement: Statement) -> Self { +impl From> for Block { + fn from(statement: Statement) -> Self { Self { statements: vec![statement] } } } -impl AsRef<[Statement]> for Block { - fn as_ref(&self) -> &[Statement] { +impl AsRef<[Statement]> for Block { + fn as_ref(&self) -> &[Statement] { self.statements.as_ref() } } @@ -123,15 +124,41 @@ pub struct FormalParam { pub enum Declaration { FuncSig(Signature), FuncDecl(Signature, Block), - TypeDecl { name: TypeSingletonName, body: TypeBody, mutable: bool }, + TypeDecl { + name: TypeSingletonName, + body: TypeBody, + mutable: bool, + }, //TODO TypeAlias `original` needs to be a more complex type definition - TypeAlias { alias: Rc, original: Rc }, - Binding { name: Rc, constant: bool, type_anno: Option, expr: Expression }, - Impl { type_name: TypeIdentifier, interface_name: Option, block: Vec }, - Interface { name: Rc, signatures: Vec }, + TypeAlias { + alias: Rc, + original: Rc, + }, + Binding { + name: Rc, + constant: bool, + type_anno: Option, + expr: Expression, + }, + Impl { + type_name: TypeIdentifier, + interface_name: Option, + block: Vec>, + }, + Interface { + name: Rc, + signatures: Vec, + }, //TODO need to limit the types of statements that can be annotated - Annotation { name: Rc, arguments: Vec, inner: Box }, - Module { name: Rc, items: Block }, + Annotation { + name: Rc, + arguments: Vec, + inner: Box>, + }, + Module { + name: Rc, + items: Block, + }, } #[derive(Debug, PartialEq, Clone)] @@ -192,6 +219,22 @@ pub enum TypeIdentifier { Singleton(TypeSingletonName), } +impl fmt::Display for TypeIdentifier { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + TypeIdentifier::Tuple(items) => + write!(f, "{}", delim_wrapped('(', ')', items.iter().map(|item| item.to_string()))), + TypeIdentifier::Singleton(tsn) => { + write!(f, "{}", tsn.name)?; + if !tsn.params.is_empty() { + write!(f, "{}", delim_wrapped('<', '>', tsn.params.iter().map(|item| item.to_string())))?; + } + Ok(()) + } + } + } +} + #[derive(Debug, PartialEq, Clone)] pub struct TypeSingletonName { pub name: Rc, diff --git a/schala-lang/src/ast/visualize.rs b/schala-lang/src/ast/visualize.rs index c14b5b4..a1c4958 100644 --- a/schala-lang/src/ast/visualize.rs +++ b/schala-lang/src/ast/visualize.rs @@ -29,7 +29,7 @@ pub(super) fn render_ast(ast: &AST) -> String { buf } -fn render_statement(stmt: &Statement, indent: usize, buf: &mut String) { +fn render_statement(stmt: &Statement, indent: usize, buf: &mut String) { use StatementKind::*; do_indent(indent, buf); match stmt.kind { diff --git a/schala-lang/src/parsing/combinator.rs b/schala-lang/src/parsing/combinator.rs index 013dbfd..488968d 100644 --- a/schala-lang/src/parsing/combinator.rs +++ b/schala-lang/src/parsing/combinator.rs @@ -163,7 +163,7 @@ pub fn block(input: Span) -> ParseResult { context("block", map(block_template(many1(statement_delimiter), statement), |items| items.into()))(input) } -fn statement(input: Span) -> ParseResult { +fn statement(input: Span) -> ParseResult> { let (input, pos) = position(input)?; let location = pos.location_offset().into(); let id = fresh_id(&input); @@ -243,8 +243,16 @@ fn implementation(input: Span) -> ParseResult { ))(input) } -fn decl_block(input: Span) -> ParseResult> { - block_template(many1(statement_delimiter), func_decl)(input) +fn decl_block(input: Span) -> ParseResult>> { + context("decl-block", block_template(many1(statement_delimiter), func_decl_statement))(input) +} + +fn func_decl_statement(input: Span) -> ParseResult> { + let (input, pos) = position(input)?; + let location = pos.location_offset().into(); + let id = fresh_id(&input); + let (rest, kind) = context("decl-statement", func_decl)(input)?; + Ok((rest, Statement { id, location, kind })) } fn interface(input: Span) -> ParseResult { diff --git a/schala-lang/src/parsing/peg_parser.rs b/schala-lang/src/parsing/peg_parser.rs index 9a6c034..4fe08ef 100644 --- a/schala-lang/src/parsing/peg_parser.rs +++ b/schala-lang/src/parsing/peg_parser.rs @@ -38,10 +38,10 @@ peg::parser! { "{" __ items:(statement(parser) ** delimiter()) delimiter()? __ "}" { items.into() } / "{" __ stmt:statement(parser) __ "}" { vec![stmt].into() } - rule block_item(parser: &mut Parser) -> Statement = + rule block_item(parser: &mut Parser) -> Statement = _ stmt:statement(parser) _ delimiter()+ { stmt } - rule statement(parser: &mut Parser) -> Statement = + rule statement(parser: &mut Parser) -> Statement = _ pos:position!() kind:statement_kind(parser) _ { Statement { id: parser.fresh(), location: pos.into(), kind } } rule statement_kind(parser: &mut Parser) -> StatementKind = @@ -100,8 +100,11 @@ peg::parser! { Declaration::Impl { type_name, interface_name: None, block } } - rule decl_block(parser: &mut Parser) -> Vec = - "{" __ decls:(func_declaration(parser) ** (delimiter()+)) delimiter()? __ "}" { decls } + rule decl_block(parser: &mut Parser) -> Vec> = + "{" __ decls:(func_declaration_stmt(parser) ** (delimiter()+)) delimiter()? __ "}" { decls } + + rule func_declaration_stmt(parser: &mut Parser) -> Statement = + pos:position!() decl:func_declaration(parser) { Statement { id: parser.fresh(), location: pos.into(), kind: decl } } rule interface(parser: &mut Parser) -> Declaration = "interface" _ name:identifier() _ signatures:signature_block(parser) { Declaration::Interface { name: rc_string(name), signatures } } diff --git a/schala-lang/src/parsing/test.rs b/schala-lang/src/parsing/test.rs index 6765a24..6091722 100644 --- a/schala-lang/src/parsing/test.rs +++ b/schala-lang/src/parsing/test.rs @@ -21,11 +21,11 @@ fn strlit(s: &str) -> ExpressionKind { ExpressionKind::StringLiteral { s: Rc::new(s.to_string()), prefix: None } } -fn stmt(kind: StatementKind) -> Statement { +fn stmt(kind: K) -> Statement { Statement { location: Location::default(), id: ItemId::default(), kind } } -fn exst(kind: ExpressionKind) -> Statement { +fn exst(kind: ExpressionKind) -> Statement { Statement { location: Location::default(), id: ItemId::default(), @@ -33,7 +33,7 @@ fn exst(kind: ExpressionKind) -> Statement { } } -fn decl(declaration: Declaration) -> Statement { +fn decl(declaration: Declaration) -> Statement { Statement { location: Location::default(), id: ItemId::default(), @@ -41,7 +41,7 @@ fn decl(declaration: Declaration) -> Statement { } } -fn fn_decl(sig: Signature, stmts: Block) -> Statement { +fn fn_decl(sig: Signature, stmts: Block) -> Statement { Statement { kind: StatementKind::Declaration(Declaration::FuncDecl(sig, stmts)), location: Default::default(), @@ -992,14 +992,14 @@ fn impls() { use Declaration::{FuncDecl, Impl}; let block = vec![ - FuncDecl( + stmt(FuncDecl( Signature { name: rc("yolo"), operator: false, params: vec![], type_anno: None }, vec![].into(), - ), - FuncDecl( + )), + stmt(FuncDecl( Signature { name: rc("swagg"), operator: false, params: vec![], type_anno: None }, vec![].into(), - ), + )), ]; assert_ast!( diff --git a/schala-lang/src/reduced_ir/mod.rs b/schala-lang/src/reduced_ir/mod.rs index 650195a..53bdbba 100644 --- a/schala-lang/src/reduced_ir/mod.rs +++ b/schala-lang/src/reduced_ir/mod.rs @@ -65,7 +65,7 @@ impl<'a, 'b> Reducer<'a, 'b> { ReducedIR { functions: self.functions, entrypoint } } - fn top_level_definition(&mut self, statement: &ast::Statement) { + fn top_level_definition(&mut self, statement: &ast::Statement) { let ast::Statement { id: item_id, kind, .. } = statement; match kind { ast::StatementKind::Expression(_expr) => { @@ -85,7 +85,10 @@ impl<'a, 'b> Reducer<'a, 'b> { } } - fn function_internal_statement(&mut self, statement: &ast::Statement) -> Option { + fn function_internal_statement( + &mut self, + statement: &ast::Statement, + ) -> Option { let ast::Statement { id: item_id, kind, .. } = statement; match kind { ast::StatementKind::Expression(expr) => Some(Statement::Expression(self.expression(expr))), diff --git a/schala-lang/src/symbol_table/mod.rs b/schala-lang/src/symbol_table/mod.rs index a11d0ba..4ae4844 100644 --- a/schala-lang/src/symbol_table/mod.rs +++ b/schala-lang/src/symbol_table/mod.rs @@ -35,6 +35,7 @@ pub enum SymbolError { DuplicateRecord { type_fqsn: Fqsn, location: Location, record: String, member: String }, UnknownAnnotation { name: String }, BadAnnotation { name: String, msg: String }, + BadImplBlockEntry, } #[allow(dead_code)] diff --git a/schala-lang/src/symbol_table/populator.rs b/schala-lang/src/symbol_table/populator.rs index c009cbc..41b61e8 100644 --- a/schala-lang/src/symbol_table/populator.rs +++ b/schala-lang/src/symbol_table/populator.rs @@ -1,5 +1,6 @@ use std::{ collections::{hash_map::Entry, HashMap, HashSet}, + rc::Rc, str::FromStr, }; @@ -38,7 +39,7 @@ impl<'a> SymbolTablePopulator<'a> { fn add_from_scope( &mut self, - statements: &[Statement], + statements: &[Statement], scope_stack: &mut Vec, function_scope: bool, ) -> Vec { @@ -75,11 +76,28 @@ impl<'a> SymbolTablePopulator<'a> { self.add_type_members(name, body, mutable, location, type_fqsn) } - /* - Declaration::Impl { type_name, body, .. } => { + Declaration::Impl { type_name, interface_name, block } => { + let mut errors = vec![]; + let new_scope = ScopeSegment::Name(Rc::new(format!("{}", type_name))); + scope_stack.push(new_scope); - }, - */ + for decl_stmt in block.iter() { + let Statement { id, kind, location } = decl_stmt; + let location = *location; + match kind { + decl @ Declaration::FuncDecl(..) => { + let output = + self.add_single_declaration(id, decl, location, scope_stack, true); + if let Err(e) = output { + errors.push(e); + }; + } + _other => errors.push(SymbolError::BadImplBlockEntry), + }; + } + scope_stack.pop(); + errors + } _ => vec![], }; errors.extend(recursive_errs.into_iter()); @@ -98,7 +116,22 @@ impl<'a> SymbolTablePopulator<'a> { function_scope: bool, ) -> Result<(), SymbolError> { match kind { - StatementKind::Declaration(Declaration::FuncSig(signature)) => { + StatementKind::Declaration(decl) => + self.add_single_declaration(id, decl, location, scope_stack, function_scope), + _ => return Ok(()), + } + } + + fn add_single_declaration( + &mut self, + id: &ItemId, + decl: &Declaration, + location: Location, + scope_stack: &[ScopeSegment], + function_scope: bool, + ) -> Result<(), SymbolError> { + match decl { + Declaration::FuncSig(signature) => { let fq_function = Fqsn::from_scope_stack(scope_stack, signature.name.clone()); self.table .fq_names @@ -109,7 +142,7 @@ impl<'a> SymbolTablePopulator<'a> { self.add_symbol(id, fq_function, SymbolSpec::Func { method: None }); } - StatementKind::Declaration(Declaration::FuncDecl(signature, ..)) => { + Declaration::FuncDecl(signature, ..) => { let fn_name = &signature.name; let fq_function = Fqsn::from_scope_stack(scope_stack, fn_name.clone()); self.table @@ -121,11 +154,13 @@ impl<'a> SymbolTablePopulator<'a> { self.add_symbol(id, fq_function, SymbolSpec::Func { method: None }); } - StatementKind::Declaration(Declaration::TypeDecl { name, .. }) => { + Declaration::TypeDecl { name, .. } => { let fq_type = Fqsn::from_scope_stack(scope_stack, name.name.clone()); self.table.types.register(fq_type, NameSpec { location, kind: TypeKind::Constructor })?; } - StatementKind::Declaration(Declaration::Binding { name, .. }) => { + //TODO handle type aliases + Declaration::TypeAlias { .. } => (), + Declaration::Binding { name, .. } => { let fq_binding = Fqsn::from_scope_stack(scope_stack, name.clone()); self.table .fq_names @@ -134,11 +169,14 @@ impl<'a> SymbolTablePopulator<'a> { self.add_symbol(id, fq_binding, SymbolSpec::GlobalBinding); } } - StatementKind::Declaration(Declaration::Module { name, .. }) => { + //TODO implement interfaces + Declaration::Interface { .. } => (), + Declaration::Impl { .. } => (), + Declaration::Module { name, .. } => { let fq_module = Fqsn::from_scope_stack(scope_stack, name.clone()); self.table.fq_names.register(fq_module, NameSpec { location, kind: NameKind::Module })?; } - StatementKind::Declaration(Declaration::Annotation { name, arguments, inner }) => { + Declaration::Annotation { name, arguments, inner } => { let inner = inner.as_ref(); self.add_single_statement( &inner.id, @@ -149,7 +187,6 @@ impl<'a> SymbolTablePopulator<'a> { )?; self.process_annotation(name.as_ref(), arguments.as_slice(), scope_stack, inner)?; } - _ => (), } Ok(()) } @@ -159,7 +196,7 @@ impl<'a> SymbolTablePopulator<'a> { name: &str, arguments: &[Expression], scope_stack: &[ScopeSegment], - inner: &Statement, + inner: &Statement, ) -> Result<(), SymbolError> { if name == "register_builtin" { if let Statement { diff --git a/schala-lang/src/symbol_table/test.rs b/schala-lang/src/symbol_table/test.rs index 77c56a4..fca51e5 100644 --- a/schala-lang/src/symbol_table/test.rs +++ b/schala-lang/src/symbol_table/test.rs @@ -271,9 +271,8 @@ fn duplicate_struct_members() { ); } -/* #[test] -fn methods() { +fn method_definition_added_to_symbol_table() { let source = r#" type Foo = { x: Int, y: Int } @@ -286,6 +285,28 @@ impl Foo { "#; let (symbols, _) = add_symbols(source); symbols.debug(); - assert!(symbols.fq_names.table.get(&make_fqsn(&["hella"])).is_some()); + assert!(symbols.fq_names.table.get(&make_fqsn(&["Foo", "hella"])).is_some()); +} + +#[test] +fn duplicate_method_definitions_detected() { + let source = r#" + +type Foo = { x: Int, y: Int } + +impl Foo { + fn hella() { + self.x + 50 + } + + fn hella() { + self.x + 40 + } +} +"#; + let (_symbols, output) = add_symbols(source); + let errs = output.unwrap_err(); + assert_matches!(&errs[..], [ + SymbolError::DuplicateName { prev_name: pn1, ..}, + ] if pn1 == &Fqsn::from_strs(&["Foo", "hella"])); } -*/ diff --git a/schala-lang/src/tree_walk_eval/mod.rs b/schala-lang/src/tree_walk_eval/mod.rs index 5d22a1e..60ecd7f 100644 --- a/schala-lang/src/tree_walk_eval/mod.rs +++ b/schala-lang/src/tree_walk_eval/mod.rs @@ -4,7 +4,7 @@ use crate::{ reduced_ir::{Callable, Expression, FunctionDefinition, Literal, ReducedIR}, symbol_table::DefId, type_inference::{TypeContext, TypeId}, - util::ScopeStack, + util::{delim_wrapped, ScopeStack}, }; mod evaluator; @@ -61,19 +61,6 @@ impl RuntimeError { } } -fn delim_wrapped(lhs: char, rhs: char, terms: impl Iterator) -> String { - let mut buf = String::new(); - write!(buf, "{}", lhs).unwrap(); - for term in terms.map(Some).intersperse(None) { - match term { - Some(e) => write!(buf, "{}", e).unwrap(), - None => write!(buf, ", ").unwrap(), - }; - } - write!(buf, "{}", rhs).unwrap(); - buf -} - /// Anything that can be stored in memory; that is, a function definition, or a fully-evaluated /// program value. #[derive(Debug)] diff --git a/schala-lang/src/util.rs b/schala-lang/src/util.rs index 1c9166f..efb2dbe 100644 --- a/schala-lang/src/util.rs +++ b/schala-lang/src/util.rs @@ -1,4 +1,18 @@ -use std::{cmp::Eq, collections::HashMap, hash::Hash}; +use std::{cmp::Eq, collections::HashMap, fmt::Write, hash::Hash}; + +/// Utility function for printing a comma-delimited list of things +pub(crate) fn delim_wrapped(lhs: char, rhs: char, terms: impl Iterator) -> String { + let mut buf = String::new(); + write!(buf, "{}", lhs).unwrap(); + for term in terms.map(Some).intersperse(None) { + match term { + Some(e) => write!(buf, "{}", e).unwrap(), + None => write!(buf, ", ").unwrap(), + }; + } + write!(buf, "{}", rhs).unwrap(); + buf +} #[derive(Default, Debug)] pub struct ScopeStack<'a, T: 'a, V: 'a, N = String>