require explicit variable declarations

This commit is contained in:
anna 2022-08-07 16:48:07 +02:00
parent cb32a2c7e2
commit 57e74ab52a
Signed by: fef
GPG key ID: EC22E476DC2D3D84
5 changed files with 96 additions and 42 deletions

View file

@ -97,6 +97,7 @@ impl Parser {
token::Kind::SourceKeyword => self.parse_source_stmt(),
token::Kind::TargetKeyword => self.parse_target_stmt(),
token::Kind::TypeKeyword => self.parse_type_stmt(),
token::Kind::VarKeyword => self.parse_decl_stmt(),
token::Kind::WhileKeyword => self.parse_while_stmt(),
k if k.is_start_of_expr() => self.parse_expr_stmt(),
_ => self.syntax_error(format!("Unexpected token {}", token), &token),
@ -147,6 +148,23 @@ impl Parser {
Ok(Node::ReturnStmt(return_keyword.pos, Box::new(expr)))
}
/// ```notrust
/// DeclarationStatement
/// : "var" Identifier [ "=" Expression ] ";"
/// ```
fn parse_decl_stmt(&mut self) -> Result<Node, Error> {
let var_keyword = self.lexer.expect_kind(token::Kind::VarKeyword)?;
let ident = self.lexer.expect_kind(token::Kind::Ident)?;
let ident = Node::Ident(ident.pos, ident.raw);
let initializer = if self.lexer.next_if(token::Kind::Eq).is_some() {
Some(self.parse_expr(&[token::Kind::Semi])?)
} else {
self.lexer.expect_kind(token::Kind::Semi)?;
None
};
Ok(Node::make_decl_stmt(var_keyword.pos, ident, initializer))
}
/// ```notrust
/// TargetStatement
/// : "target" Expression BlockStatement
@ -163,11 +181,7 @@ impl Parser {
let children = self.parse_block_stmt()?;
self.scope.pop();
Ok(Node::make_target_stmt(
target_keyword.pos,
name,
children,
))
Ok(Node::make_target_stmt(target_keyword.pos, name, children))
}
/// ```notrust
@ -216,11 +230,7 @@ impl Parser {
let condition = self.parse_expr(&[token::Kind::CParen])?;
let body = self.parse_block_stmt()?;
self.scope.pop();
Ok(Node::make_while_stmt(
while_keyword.pos,
condition,
body,
))
Ok(Node::make_while_stmt(while_keyword.pos, condition, body))
}
/// ```notrust
@ -265,11 +275,7 @@ impl Parser {
match expr {
Node::BinaryExpr(_, expr) => {
if expr.op == Operator::Eq {
Ok(Node::make_set_stmt(
set_keyword.pos,
*expr.lhs,
*expr.rhs,
))
Ok(Node::make_set_stmt(set_keyword.pos, *expr.lhs, *expr.rhs))
} else {
self.syntax_error(format!("Invalid operator"), self.lexer.current().unwrap())
}
@ -393,10 +399,7 @@ impl Parser {
/// : "||" | "&&" | "==" | "!=" | "<" | "<=" | ">" | ">="
/// : "|" | "^" | "&" | "<<" | ">>" | "+" | "-" | "*" | "/" | "%"
/// ```
fn parse_binary_expr_or_higher(
&mut self,
terminators: &[token::Kind],
) -> Result<Node, Error> {
fn parse_binary_expr_or_higher(&mut self, terminators: &[token::Kind]) -> Result<Node, Error> {
let mut lhs = self.parse_unary_expr_or_higher()?;
while let Some(Ok(token)) = self.lexer.peek() {
@ -569,17 +572,13 @@ impl Parser {
self.lexer.next();
let params =
self.parse_delimited_list(token::Kind::Comma, token::Kind::CParen, false)?;
self.parse_primary_expr_rest(Node::make_call_expr(
token.pos, start, params,
))
self.parse_primary_expr_rest(Node::make_call_expr(token.pos, start, params))
}
token::Kind::OBracket => {
// array index
self.lexer.next();
let index = self.parse_expr(&[token::Kind::CBracket])?;
self.parse_primary_expr_rest(Node::make_array_expr(
token.pos, start, index,
))
self.parse_primary_expr_rest(Node::make_array_expr(token.pos, start, index))
}
token::Kind::Dot => {
// member access

View file

@ -23,6 +23,7 @@ pub enum Node {
Fn(Position, Arc<FnStmt>),
ArrayExpr(Position, ArrayExpr),
CallExpr(Position, CallExpr),
DeclStmt(Position, DeclStmt),
IfStmt(Position, IfStmt),
ReturnStmt(Position, Box<Node>),
ForStmt(Position, ForStmt),
@ -40,6 +41,11 @@ pub struct File {
pub content: Vec<Node>,
}
pub struct DeclStmt {
pub name: Box<Node>,
pub val: Option<Box<Node>>,
}
pub struct FnStmt {
pub name: Option<Box<Node>>,
pub params: Vec<Node>,
@ -170,6 +176,7 @@ impl Node {
Node::IfStmt(pos, _) => pos,
Node::ForStmt(pos, _) => pos,
Node::WhileStmt(pos, _) => pos,
Node::DeclStmt(pos, _) => pos,
Node::UnaryExpr(pos, _) => pos,
Node::BinaryExpr(pos, _) => pos,
Node::TypeStmt(pos, _) => pos,
@ -241,6 +248,12 @@ impl Node {
stmt.body.visit(cb, depth);
}
Node::ReturnStmt(_, node) => node.visit(cb, depth),
Node::DeclStmt(_, stmt) => {
stmt.name.visit(cb, depth);
if let Some(val) = &stmt.val {
val.visit(cb, depth);
}
}
Node::UnaryExpr(_, expr) => expr.rhs.visit(cb, depth),
Node::BinaryExpr(_, expr) => {
expr.lhs.visit(cb, depth);
@ -321,6 +334,21 @@ impl Node {
)
}
pub fn make_decl_stmt(pos: Position, name: Node, val: Option<Node>) -> Node {
let val = if let Some(val) = val {
Some(Box::new(val))
} else {
None
};
Node::DeclStmt(
pos,
DeclStmt {
name: Box::new(name),
val,
},
)
}
pub fn make_set_stmt(pos: Position, name: Node, val: Node) -> Node {
Node::SetStmt(
pos,
@ -435,6 +463,7 @@ impl fmt::Display for Node {
Node::IfStmt(_, _) => "<if>",
Node::ForStmt(_, _) => "<for>",
Node::WhileStmt(_, _) => "<while>",
Node::DeclStmt(_, _) => "<decl>",
Node::UnaryExpr(_, expr) => expr.op.raw(),
Node::BinaryExpr(_, expr) => expr.op.raw(),
Node::TypeStmt(_, _) => "type",

View file

@ -35,7 +35,7 @@ const fn kw(raw: &'static str, kind: token::Kind) -> KeywordMap {
KeywordMap { raw, kind }
}
static KEYWORDS: [KeywordMap; 15] = [
static KEYWORDS: [KeywordMap; 16] = [
kw("break", token::Kind::BreakKeyword),
kw("depend", token::Kind::DependKeyword),
kw("else", token::Kind::ElseKeyword),
@ -50,6 +50,7 @@ static KEYWORDS: [KeywordMap; 15] = [
kw("target", token::Kind::TargetKeyword),
kw("true", token::Kind::TrueKeyword),
kw("type", token::Kind::TypeKeyword),
kw("var", token::Kind::VarKeyword),
kw("while", token::Kind::WhileKeyword),
];

View file

@ -83,6 +83,7 @@ pub enum Kind {
TargetKeyword,
TrueKeyword,
TypeKeyword,
VarKeyword,
WhileKeyword,
StringLiteral,
@ -234,6 +235,7 @@ impl fmt::Display for Kind {
Kind::TargetKeyword => "keyword",
Kind::TrueKeyword => "keyword",
Kind::TypeKeyword => "keyword",
Kind::VarKeyword => "keyword",
Kind::WhileKeyword => "keyword",
Kind::StringLiteral => "string",

View file

@ -11,16 +11,11 @@ use val::{Scope, Val, ValOps};
/// Execute a program.
/// The `root` node must be a `Node::File`.
/// This is just a wrapper for `exec_file()` that removes all the
/// obvious boilerplate we don't need.
pub fn exec(root: &Node) -> Result<Val, Error> {
match root {
Node::File(pos, f) => {
let mut root_scope = Scope::new_root(pos.file.clone());
let mut val = Val::new(());
for node in &f.content {
val = node.eval(&mut root_scope)?;
}
Ok(val)
}
Node::File(pos, f) => exec_file(pos.file.clone(), &f.content),
node => Err(Error::runtime_error(
empty_pos(),
format!("cannot exec non-file node {}", node),
@ -28,6 +23,16 @@ pub fn exec(root: &Node) -> Result<Val, Error> {
}
}
/// Actually execute a program.
fn exec_file(filename: String, content: &Vec<Node>) -> Result<Val, Error> {
let mut root_scope = Scope::new_root(filename);
let mut val = Val::new(());
for node in content {
val = node.eval(&mut root_scope)?;
}
Ok(val)
}
impl Node {
fn eval(&self, scope: &mut Scope) -> Result<Val, Error> {
match self {
@ -88,15 +93,26 @@ impl Node {
},
&stmt.body,
),
Node::DeclStmt(_, stmt) => {
let name = stmt.name.assert_ident()?;
let val = if let Some(val) = &stmt.val {
val.eval(scope)?
} else {
Val::new(())
};
scope.define(name.clone(), val);
Ok(Val::new(()))
}
Node::CallExpr(_, expr) => {
// we don't execute the function within the callback because
// (1) that would block the mutex for way longer than necessary and
// (2) recursive function calls would cause a deadlock (ask me how i know)
let (func, mut capture) = expr.func.eval(scope)?.as_fn(|func, capture| {
(func.clone(), capture.clone())
})?;
let (func, mut capture) = expr
.func
.eval(scope)?
.as_fn(|func, capture| (func.clone(), capture.clone()))?;
exec_fn(scope, &mut capture, &func.body, &func.params, &expr.params)
},
}
Node::ArrayExpr(pos, expr) => expr.array.eval(scope)?.as_array(|array| {
expr.index.eval(scope)?.as_int(|index| {
if *index >= 0 && *index < array.len() as i128 {
@ -177,14 +193,21 @@ impl Operator {
match self {
Operator::Eq => match lhs {
Node::Ident(pos, name) => {
let val = rhs.eval(scope)?;
match val.typ() {
let new_val = rhs.eval(scope)?;
match new_val.typ() {
Type::Void => {
Err(Error::type_error(pos.clone(), Type::Unknown, Type::Void))
}
_ => {
scope.define(name.clone(), val);
Ok(Val::new(()))
if let Some(val) = scope.resolve(name) {
val.update(&new_val);
Ok(Val::new(()))
} else {
Err(Error::runtime_error(
pos.clone(),
format!("Identifier \"{}\" not found in this scope", name),
))
}
}
}
}