diff --git a/TODO.md b/TODO.md index a721ed3..9b3ecf7 100644 --- a/TODO.md +++ b/TODO.md @@ -23,10 +23,6 @@ 2. Once FQSNs are aware of function parameters, most of the Rc things in eval.rs can go away -## Semantics - -* Use annotations to indicate builtin functions - ## Parser * I think I can restructure the parser to get rid of most instances of expect!, at least at the beginning of a rule diff --git a/schala-lang/language/source-files/prelude.schala b/schala-lang/language/source-files/prelude.schala index 0a6c93f..a2208d2 100644 --- a/schala-lang/language/source-files/prelude.schala +++ b/schala-lang/language/source-files/prelude.schala @@ -3,6 +3,14 @@ let _SCHALA_VERSION = "0.1.0" type Option = Some(T) | None type Ord = LT | EQ | GT +@register_builtin(print) +fn print(arg) { } + +@register_builtin(println) +fn println(arg) { } + +@register_builtin(getline) +fn getline(arg) { } fn map(input: Option, func: Func): Option { if input { diff --git a/schala-lang/language/src/ast/mod.rs b/schala-lang/language/src/ast/mod.rs index 05ea5c2..0bf76b2 100644 --- a/schala-lang/language/src/ast/mod.rs +++ b/schala-lang/language/src/ast/mod.rs @@ -53,6 +53,7 @@ pub enum StatementKind { Expression(Expression), Declaration(Declaration), Import(ImportSpecifier), + //TODO maybe modules shoudl be a type of declaration Module(ModuleSpecifier), Flow(FlowControl), } @@ -130,7 +131,8 @@ pub enum Declaration { Binding { name: Rc, constant: bool, type_anno: Option, expr: Expression }, Impl { type_name: TypeIdentifier, interface_name: Option, block: Vec }, Interface { name: Rc, signatures: Vec }, - Annotation { name: Rc, arguments: Vec, inner: Box }, + //TODO need to limit the types of statements that can be annotated + Annotation { name: Rc, arguments: Vec, inner: Box }, } #[derive(Debug, PartialEq, Clone)] diff --git a/schala-lang/language/src/parsing/mod.rs b/schala-lang/language/src/parsing/mod.rs index b7c050d..0f716de 100644 --- a/schala-lang/language/src/parsing/mod.rs +++ b/schala-lang/language/src/parsing/mod.rs @@ -445,7 +445,7 @@ impl Parser { if let Semicolon | Newline = self.token_handler.peek_kind() { self.token_handler.next(); } - let inner = Box::new(self.declaration()?); + let inner = Box::new(self.statement()?); Ok(Declaration::Annotation { name, arguments, inner }) } diff --git a/schala-lang/language/src/parsing/test.rs b/schala-lang/language/src/parsing/test.rs index 91bf309..4eb8194 100644 --- a/schala-lang/language/src/parsing/test.rs +++ b/schala-lang/language/src/parsing/test.rs @@ -928,10 +928,10 @@ fn impls() { fn annotations() { use ExpressionKind::*; - let func = Declaration::FuncDecl( + let func = decl(Declaration::FuncDecl( Signature { name: rc("some_function"), operator: false, params: vec![], type_anno: None }, vec![].into(), - ); + )); assert_ast! { r#" @@ -957,9 +957,9 @@ fn annotations() { vec![decl(Declaration::Annotation { name: rc("test_annotation"), arguments: vec![expr(Value(qn!(some))), expr(Value(qn!(value)))], - inner: bx(Declaration::Annotation { + inner: bx(decl(Declaration::Annotation { name: rc("another_annotation"), arguments: vec![], inner: bx(func) - }) + })) }), ] }; diff --git a/schala-lang/language/src/symbol_table/mod.rs b/schala-lang/language/src/symbol_table/mod.rs index e818ae2..ed40eea 100644 --- a/schala-lang/language/src/symbol_table/mod.rs +++ b/schala-lang/language/src/symbol_table/mod.rs @@ -4,13 +4,14 @@ use std::{ collections::{hash_map::Entry, HashMap, HashSet}, fmt, rc::Rc, + str::FromStr, }; use crate::{ ast, ast::{ - Declaration, ItemId, ModuleSpecifier, Statement, StatementKind, TypeBody, TypeSingletonName, Variant, - VariantKind, + Declaration, Expression, ExpressionKind, ItemId, ModuleSpecifier, Statement, StatementKind, TypeBody, + TypeSingletonName, Variant, VariantKind, }, builtin::Builtin, tokenizing::Location, @@ -43,6 +44,7 @@ impl Fqsn { Fqsn { scopes: v } } + #[allow(dead_code)] fn from_strs(strs: &[&str]) -> Fqsn { let mut scopes = vec![]; for s in strs { @@ -51,7 +53,7 @@ impl Fqsn { Fqsn { scopes } } - fn local_name(&self) -> Rc { + fn last_elem(&self) -> Rc { let ScopeSegment::Name(name) = self.scopes.last().unwrap(); name.clone() } @@ -89,6 +91,8 @@ pub enum SymbolError { DuplicateName { prev_name: Fqsn, location: Location }, DuplicateVariant { type_fqsn: Fqsn, name: String }, DuplicateRecord { type_name: Fqsn, location: Location, member: String }, + UnknownAnnotation { name: String }, + BadAnnotation { name: String, msg: String }, } #[allow(dead_code)] @@ -221,11 +225,13 @@ impl SymbolTable { } fn populate_builtins(&mut self) { + /* let fqsn = Fqsn::from_strs(&["println"]); self.populate_single_builtin(fqsn, Builtin::IOPrintLn); let fqsn = Fqsn::from_strs(&["print"]); self.populate_single_builtin(fqsn, Builtin::IOPrint); + */ } } @@ -244,7 +250,7 @@ pub struct Symbol { impl Symbol { pub fn local_name(&self) -> Rc { - self.fully_qualified_name.local_name() + self.fully_qualified_name.last_elem() } pub fn def_id(&self) -> Option { @@ -411,11 +417,68 @@ impl<'a> SymbolTableRunner<'a> { 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 }) => { + let inner = inner.as_ref(); + self.add_single_statement( + &inner.id, + &inner.kind, + inner.location, + scope_stack, + function_scope, + )?; + self.process_annotation(name.as_ref(), arguments.as_slice(), scope_stack, inner)?; + } _ => (), } Ok(()) } + fn process_annotation( + &mut self, + name: &str, + arguments: &[Expression], + scope_stack: &[ScopeSegment], + inner: &Statement, + ) -> Result<(), SymbolError> { + println!("handling annotation: {}", name); + if name == "register_builtin" { + if let Statement { + id: _, + location: _, + kind: StatementKind::Declaration(Declaration::FuncDecl(sig, _)), + } = inner + { + let fqsn = Fqsn::from_scope_stack(scope_stack, sig.name.clone()); + let builtin_name = match arguments { + [Expression { kind: ExpressionKind::Value(qname), .. }] + if qname.components.len() == 1 => + qname.components[0].clone(), + _ => + return Err(SymbolError::BadAnnotation { + name: name.to_string(), + msg: "Bad argument for register_builtin".to_string(), + }), + }; + + let builtin = + Builtin::from_str(builtin_name.as_str()).map_err(|_| SymbolError::BadAnnotation { + name: name.to_string(), + msg: format!("Invalid builtin: {}", builtin_name), + })?; + + self.table.populate_single_builtin(fqsn, builtin); + Ok(()) + } else { + Err(SymbolError::BadAnnotation { + name: name.to_string(), + msg: "register_builtin not annotating a function".to_string(), + }) + } + } else { + Err(SymbolError::UnknownAnnotation { name: name.to_string() }) + } + } + fn add_type_members( &mut self, type_name: &TypeSingletonName, diff --git a/schala-lang/language/src/symbol_table/resolver.rs b/schala-lang/language/src/symbol_table/resolver.rs index b4ad4ca..89aece4 100644 --- a/schala-lang/language/src/symbol_table/resolver.rs +++ b/schala-lang/language/src/symbol_table/resolver.rs @@ -99,13 +99,13 @@ impl<'a> ASTVisitor for ScopeResolver<'a> { Fqsn { scopes: path_components.iter().map(|c| ScopeSegment::Name(c.clone())).collect() }; let members = self.symbol_table.symbol_trie.get_children(&prefix); for fqsn in members.into_iter() { - self.lexical_scopes.insert(fqsn.local_name(), NameType::Import(fqsn)); + self.lexical_scopes.insert(fqsn.last_elem(), NameType::Import(fqsn)); } } ImportedNames::LastOfPath => { let fqsn = Fqsn { scopes: path_components.iter().map(|c| ScopeSegment::Name(c.clone())).collect() }; - self.lexical_scopes.insert(fqsn.local_name(), NameType::Import(fqsn)); + self.lexical_scopes.insert(fqsn.last_elem(), NameType::Import(fqsn)); } ImportedNames::List(ref names) => { let fqsn_prefix: Vec = @@ -114,7 +114,7 @@ impl<'a> ASTVisitor for ScopeResolver<'a> { let mut scopes = fqsn_prefix.clone(); scopes.push(ScopeSegment::Name(name.clone())); let fqsn = Fqsn { scopes }; - self.lexical_scopes.insert(fqsn.local_name(), NameType::Import(fqsn)); + self.lexical_scopes.insert(fqsn.last_elem(), NameType::Import(fqsn)); } } };