From 34abb9b08116029c4f57d550a751e7f5750aff04 Mon Sep 17 00:00:00 2001 From: greg Date: Sat, 31 Aug 2019 23:39:01 -0700 Subject: [PATCH] Start work on qualified names --- schala-lang/language/src/ast.rs | 7 ++-- schala-lang/language/src/parsing.rs | 27 +++++++++++--- schala-lang/language/src/parsing/test.rs | 8 ++--- schala-lang/language/src/reduced_ast.rs | 46 ++++++++++++++++++------ schala-lang/language/src/symbol_table.rs | 4 +-- schala-lang/language/src/typechecking.rs | 8 +++-- 6 files changed, 73 insertions(+), 27 deletions(-) diff --git a/schala-lang/language/src/ast.rs b/schala-lang/language/src/ast.rs index dd4840f..cdd9175 100644 --- a/schala-lang/language/src/ast.rs +++ b/schala-lang/language/src/ast.rs @@ -46,6 +46,9 @@ pub enum Statement { pub type Block = Vec>; pub type ParamName = Rc; +#[derive(Debug, PartialEq, Clone)] +pub struct QualifiedName(pub Vec>); + #[derive(Debug, PartialEq, Clone)] pub struct FormalParam { pub name: ParamName, @@ -138,9 +141,9 @@ pub enum ExpressionKind { BinExp(BinOp, Box>, Box>), PrefixExp(PrefixOp, Box>), TupleLiteral(Vec>), - Value(Rc), + Value(QualifiedName), NamedStruct { - name: Rc, + name: QualifiedName, fields: Vec<(Rc, Meta)>, }, Call { diff --git a/schala-lang/language/src/parsing.rs b/schala-lang/language/src/parsing.rs index 26b83a5..7e68045 100644 --- a/schala-lang/language/src/parsing.rs +++ b/schala-lang/language/src/parsing.rs @@ -85,13 +85,14 @@ //! lambda_param_list := formal_param_list | formal_param //! paren_expr := "(" paren_inner ")" //! paren_inner := (expression ",")* -//! identifier_expr := named_struct | IDENTIFIER +//! identifier_expr := qualified_identifier | named_struct +//! qualified_identifier := IDENTIFIER ("::" IDENTIFIER)* //! ``` //! //! ## Literals //! ```text //! literal := "true" | "false" | number_literal | STR_LITERAL -//! named_struct := IDENTIFIER record_block +//! named_struct := qualified_identifier record_block //! record_block := "{" (record_entry, ",")* | "}" //TODO support anonymus structs, update syntax //! record_entry := IDENTIFIER ":" expression //! anonymous_struct := TODO @@ -213,6 +214,7 @@ impl TokenHandler { fn peek(&mut self) -> Token { self.tokens.get(self.idx).map(|t: &Token| { t.clone()}).unwrap_or(Token { kind: TokenKind::EOF, line_num: self.end_of_file.0, char_num: self.end_of_file.1}) } + /// calling peek_n(0) is the same thing as peek() fn peek_n(&mut self, n: usize) -> Token { self.tokens.get(self.idx + n).map(|t: &Token| { t.clone()}).unwrap_or(Token { kind: TokenKind::EOF, line_num: self.end_of_file.0, char_num: self.end_of_file.1}) } @@ -757,16 +759,31 @@ impl Parser { #[recursive_descent_method] fn identifier_expr(&mut self) -> ParseResult { use self::ExpressionKind::*; - let identifier = self.identifier()?; + let qualified_identifier = self.qualified_identifier()?; Ok(match self.token_handler.peek_kind() { LCurlyBrace if !self.restrictions.no_struct_literal => { let fields = self.record_block()?; - Expression::new(NamedStruct { name: identifier, fields }) + Expression::new(NamedStruct { name: qualified_identifier, fields }) }, - _ => Expression::new(Value(identifier)) + _ => Expression::new(Value(qualified_identifier)) }) } + #[recursive_descent_method] + fn qualified_identifier(&mut self) -> ParseResult { + let mut vec = vec![self.identifier()?]; + loop { + match (self.token_handler.peek_kind(), self.token_handler.peek_kind_n(1)) { + (Colon, Colon) => { + self.token_handler.next(); self.token_handler.next(); + vec.push(self.identifier()?); + }, + _ => break, + } + } + Ok(QualifiedName(vec)) + } + #[recursive_descent_method] fn record_block(&mut self) -> ParseResult, Meta)>> { Ok( diff --git a/schala-lang/language/src/parsing/test.rs b/schala-lang/language/src/parsing/test.rs index 33363eb..ab07aec 100644 --- a/schala-lang/language/src/parsing/test.rs +++ b/schala-lang/language/src/parsing/test.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use super::tokenize; use super::ParseResult; -use crate::ast::{AST, Meta, Expression, Statement, IfExpressionBody, Discriminator, Pattern, PatternLiteral, TypeBody, Enumerator, ForBody, InvocationArgument, FormalParam, PrefixOp, BinOp}; +use crate::ast::{AST, Meta, Expression, Statement, IfExpressionBody, Discriminator, Pattern, PatternLiteral, TypeBody, Enumerator, ForBody, InvocationArgument, FormalParam, PrefixOp, BinOp, QualifiedName}; use super::Statement::*; use super::Declaration::*; use super::Signature; @@ -30,7 +30,7 @@ macro_rules! parse_error { ($string:expr) => { assert!(parse($string).is_err()) } } macro_rules! val { - ($var:expr) => { Value(Rc::new($var.to_string())) } + ($var:expr) => { Value(QualifiedName(vec![Rc::new($var.to_string())])) }; } macro_rules! ty { ($name:expr) => { Singleton(tys!($name)) } @@ -153,11 +153,11 @@ fn parsing_identifiers() { parse_test!("None", AST(vec![exst!(val!("None"))])); parse_test!("Pandas { a: x + y }", AST(vec![ - exst!(NamedStruct { name: rc!(Pandas), fields: vec![(rc!(a), ex!(m binexp!("+", val!("x"), val!("y"))))]}) + exst!(NamedStruct { name: QualifiedName(vec![rc!(Pandas)]), fields: vec![(rc!(a), ex!(m binexp!("+", val!("x"), val!("y"))))]}) ])); parse_test! { "Pandas { a: n, b: q, }", AST(vec![ - exst!(NamedStruct { name: rc!(Pandas), fields: + exst!(NamedStruct { name: QualifiedName(vec![rc!(Pandas)]), fields: vec![(rc!(a), ex!(m val!("n"))), (rc!(b), ex!(m val!("q")))] } ) diff --git a/schala-lang/language/src/reduced_ast.rs b/schala-lang/language/src/reduced_ast.rs index 5b58a1a..bf1153d 100644 --- a/schala-lang/language/src/reduced_ast.rs +++ b/schala-lang/language/src/reduced_ast.rs @@ -16,7 +16,7 @@ use std::rc::Rc; use std::str::FromStr; use crate::ast::*; -use crate::symbol_table::{Symbol, SymbolSpec, SymbolTable}; +use crate::symbol_table::{Symbol, SymbolSpec, SymbolTable, ScopeSegment, ScopeSegmentKind, FullyQualifiedSymbolName}; use crate::builtin::Builtin; #[derive(Debug)] @@ -142,6 +142,21 @@ impl InvocationArgument { } } +//TODO this is incomplete +fn lookup_name_in_scope(sym_name: &QualifiedName) -> FullyQualifiedSymbolName { + let QualifiedName(vec) = sym_name; + let len = vec.len(); + let new_vec: Vec = vec.iter().enumerate().map(|(i, name)| { + let kind = if i == (len - 1) { + ScopeSegmentKind::Terminal + } else { + ScopeSegmentKind::Type + }; + ScopeSegment { name: name.clone(), kind } + }).collect(); + FullyQualifiedSymbolName(new_vec) +} + impl Expression { fn reduce(&self, symbol_table: &SymbolTable) -> Expr { use crate::ast::ExpressionKind::*; @@ -153,14 +168,19 @@ impl Expression { BoolLiteral(b) => Expr::Lit(Lit::Bool(*b)), BinExp(binop, lhs, rhs) => binop.reduce(symbol_table, lhs, rhs), PrefixExp(op, arg) => op.reduce(symbol_table, arg), - Value(name) => match symbol_table.lookup_by_name(name) { - Some(Symbol { spec: SymbolSpec::DataConstructor { index, type_args, type_name}, .. }) => Expr::Constructor { - type_name: type_name.clone(), - name: name.clone(), - tag: index.clone(), - arity: type_args.len(), - }, - _ => Expr::Sym(name.clone()), + Value(qualified_name) => { + let sym_name = lookup_name_in_scope(&qualified_name); + let FullyQualifiedSymbolName(ref v) = sym_name; + let name = v.last().unwrap().name.clone(); + match symbol_table.lookup_by_fqsn(&sym_name) { + Some(Symbol { spec: SymbolSpec::DataConstructor { index, type_args, type_name}, .. }) => Expr::Constructor { + type_name: type_name.clone(), + name: name.clone(), + tag: index.clone(), + arity: type_args.len(), + }, + _ => Expr::Sym(name.clone()), + } }, Call { f, arguments } => reduce_call_expression(f, arguments, symbol_table), TupleLiteral(exprs) => Expr::Tuple(exprs.iter().map(|e| e.node().reduce(symbol_table)).collect()), @@ -183,8 +203,12 @@ fn reduce_lambda(params: &Vec, body: &Block, symbol_table: &SymbolT }) } -fn reduce_named_struct(name: &Rc, fields: &Vec<(Rc, Meta)>, symbol_table: &SymbolTable) -> Expr { - let (type_name, index, members_from_table) = match symbol_table.lookup_by_name(name) { +fn reduce_named_struct(name: &QualifiedName, fields: &Vec<(Rc, Meta)>, symbol_table: &SymbolTable) -> Expr { + + let sym_name = lookup_name_in_scope(name); + let FullyQualifiedSymbolName(ref v) = sym_name; + let ref name = v.last().unwrap().name; + let (type_name, index, members_from_table) = match symbol_table.lookup_by_fqsn(&sym_name) { Some(Symbol { spec: SymbolSpec::RecordConstructor { members, type_name, index }, .. }) => (type_name.clone(), index, members), _ => return Expr::ReductionError("Not a record constructor".to_string()), }; diff --git a/schala-lang/language/src/symbol_table.rs b/schala-lang/language/src/symbol_table.rs index 2095792..c82c5f1 100644 --- a/schala-lang/language/src/symbol_table.rs +++ b/schala-lang/language/src/symbol_table.rs @@ -26,8 +26,8 @@ impl fmt::Display for FullyQualifiedSymbolName { #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct ScopeSegment { - name: Rc, //TODO maybe this could be a &str, for efficiency? - kind: ScopeSegmentKind, + pub name: Rc, //TODO maybe this could be a &str, for efficiency? + pub kind: ScopeSegmentKind, } impl fmt::Display for ScopeSegment { diff --git a/schala-lang/language/src/typechecking.rs b/schala-lang/language/src/typechecking.rs index 435f19d..88b8fa4 100644 --- a/schala-lang/language/src/typechecking.rs +++ b/schala-lang/language/src/typechecking.rs @@ -412,10 +412,12 @@ impl<'a> TypeContext<'a> { Ok(output) } - fn handle_value(&mut self, val: &Rc) -> InferResult { - match self.variable_map.lookup(val) { + fn handle_value(&mut self, val: &QualifiedName) -> InferResult { + let QualifiedName(vec) = val; + let var = &vec[0]; + match self.variable_map.lookup(var) { Some(ty) => Ok(ty.clone()), - None => TypeError::new(format!("Couldn't find variable: {}", val)) + None => TypeError::new(format!("Couldn't find variable: {}", &var)), } }