add functions

main
anna 2 years ago
parent 6d5e7cb406
commit 620be5dba1
Signed by: fef
GPG Key ID: EC22E476DC2D3D84

@ -15,6 +15,7 @@ enum Scope {
Target, Target,
DepList, DepList,
SourceList, SourceList,
Function,
} }
struct Parser { struct Parser {
@ -79,12 +80,14 @@ impl Parser {
let token = self.lexer.peek_or_err()?; let token = self.lexer.peek_or_err()?;
match token.kind { match token.kind {
token::Kind::DependKeyword => self.parse_depend_stmt(), token::Kind::DependKeyword => self.parse_depend_stmt(),
token::Kind::FnKeyword => self.parse_fn(false),
token::Kind::IfKeyword => self.parse_if_stmt(), token::Kind::IfKeyword => self.parse_if_stmt(),
token::Kind::ReturnKeyword => self.parse_return_stmt(),
token::Kind::SetKeyword => self.parse_set_stmt(), token::Kind::SetKeyword => self.parse_set_stmt(),
token::Kind::SourceKeyword => self.parse_source_stmt(), token::Kind::SourceKeyword => self.parse_source_stmt(),
token::Kind::TargetKeyword => self.parse_target_stmt(), token::Kind::TargetKeyword => self.parse_target_stmt(),
token::Kind::TypeKeyword => self.parse_type_stmt(), token::Kind::TypeKeyword => self.parse_type_stmt(),
token::Kind::Ident => self.parse_expr_stmt(), k if k.is_start_of_expr() => self.parse_expr_stmt(),
_ => self.syntax_error(format!("Unexpected token {}", token), &token), _ => self.syntax_error(format!("Unexpected token {}", token), &token),
} }
} }
@ -111,6 +114,17 @@ impl Parser {
Ok(tree::Node::Block(nodes)) Ok(tree::Node::Block(nodes))
} }
/// ```notrust
/// ReturnStatement
/// : "return" Expression ";"
/// ```
fn parse_return_stmt(&mut self) -> Result<tree::Node, Error> {
self.assert_scope(Scope::Function)?;
self.lexer.expect_kind(token::Kind::ReturnKeyword)?;
let expr = self.parse_expr(&[token::Kind::Semi])?;
Ok(tree::Node::ReturnStmt(Box::new(expr)))
}
/// ```notrust /// ```notrust
/// TargetStatement /// TargetStatement
/// : "target" Expression BlockStatement /// : "target" Expression BlockStatement
@ -452,6 +466,10 @@ impl Parser {
token::Kind::StringLiteral => Ok(tree::Node::String(token.raw)), token::Kind::StringLiteral => Ok(tree::Node::String(token.raw)),
token::Kind::TrueKeyword => Ok(tree::Node::Bool(true)), token::Kind::TrueKeyword => Ok(tree::Node::Bool(true)),
token::Kind::FalseKeyword => Ok(tree::Node::Bool(false)), token::Kind::FalseKeyword => Ok(tree::Node::Bool(false)),
token::Kind::FnKeyword => {
self.lexer.prev(); // parse_fn() expects to consume the keyword
self.parse_fn(true)
}
token::Kind::OBracket => { token::Kind::OBracket => {
let elements = let elements =
self.parse_delimited_list(token::Kind::Comma, token::Kind::CBracket, true)?; self.parse_delimited_list(token::Kind::Comma, token::Kind::CBracket, true)?;
@ -469,6 +487,7 @@ impl Parser {
/// matrix[y][x] /// matrix[y][x]
/// array_of_functions[index](params) /// array_of_functions[index](params)
/// function_returning_an_array(params)[index] /// function_returning_an_array(params)[index]
/// (fn(a, b) { return a + b; })(1, 2)
/// ``` /// ```
fn parse_primary_expr_rest(&mut self, start: tree::Node) -> Result<tree::Node, Error> { fn parse_primary_expr_rest(&mut self, start: tree::Node) -> Result<tree::Node, Error> {
if let Some(Ok(token)) = self.lexer.peek() { if let Some(Ok(token)) = self.lexer.peek() {
@ -499,6 +518,56 @@ impl Parser {
} }
} }
/// ```notrust
/// Function
/// : "fn" [ Identifier ] "(" [ ParameterList ] ")" BlockStatement
///
/// ParameterList
/// : Identifier [ "," ParameterList ]
/// ```
fn parse_fn(&mut self, allow_anonymous: bool) -> Result<tree::Node, Error> {
self.scope.push(Scope::Function);
self.lexer.expect_kind(token::Kind::FnKeyword)?;
// function name is optional (there are inline anonymous functions)
let name = if let Some(Ok(token)) = self.lexer.peek() {
if token.kind == token::Kind::Ident {
self.lexer.next();
Some(Box::new(tree::Node::Ident(token.raw)))
} else {
None
}
} else {
None
};
let oparen = self.lexer.expect_kind(token::Kind::OParen)?;
if name.is_none() && !allow_anonymous {
// anonymous function are not allowed for definitions as a block
// statement (you can only do that with inline functions)
return self.syntax_error(String::from("Function name required"), &oparen);
}
let params = self.parse_delimited_list(token::Kind::Comma, token::Kind::CParen, false)?;
for p in &params {
match p {
tree::Node::Ident(_) => continue,
_ => {
return self
.syntax_error(format!("Not an identifier"), &self.lexer.current().unwrap())
}
}
}
let body = self.parse_block_stmt()?;
self.scope.pop();
Ok(tree::Node::Fn {
name,
params,
body: Box::new(body),
})
}
/// Parse a terminated, delimited list of expressions. This is used for /// Parse a terminated, delimited list of expressions. This is used for
/// parameter lists in function calls and elements in array literals. /// parameter lists in function calls and elements in array literals.
fn parse_delimited_list( fn parse_delimited_list(

@ -16,6 +16,11 @@ pub enum Node {
String(String), String(String),
Array(Vec<Node>), Array(Vec<Node>),
Block(Vec<Node>), Block(Vec<Node>),
Fn {
name: Option<Box<Node>>,
params: Vec<Node>,
body: Box<Node>,
},
ArrayExpr { ArrayExpr {
// array access // array access
array: Box<Node>, array: Box<Node>,
@ -31,6 +36,7 @@ pub enum Node {
then_block: Box<Node>, then_block: Box<Node>,
else_block: Option<Box<Node>>, else_block: Option<Box<Node>>,
}, },
ReturnStmt(Box<Node>),
UnaryExpr { UnaryExpr {
op: Operator, op: Operator,
node: Box<Node>, node: Box<Node>,
@ -110,6 +116,15 @@ impl Node {
node.visit(cb, depth); node.visit(cb, depth);
} }
} }
Node::Fn { name, params, body } => {
if let Some(n) = name {
n.visit(cb, depth);
}
for p in params {
p.visit(cb, depth);
}
body.visit(cb, depth);
}
Node::ArrayExpr { array, index } => { Node::ArrayExpr { array, index } => {
array.visit(cb, depth); array.visit(cb, depth);
index.visit(cb, depth); index.visit(cb, depth);
@ -131,6 +146,7 @@ impl Node {
eb.visit(cb, depth); eb.visit(cb, depth);
} }
} }
Node::ReturnStmt(node) => node.visit(cb, depth),
Node::UnaryExpr { op, node } => node.visit(cb, depth), Node::UnaryExpr { op, node } => node.visit(cb, depth),
Node::BinaryExpr { op, lhs, rhs } => { Node::BinaryExpr { op, lhs, rhs } => {
lhs.visit(cb, depth); lhs.visit(cb, depth);
@ -173,6 +189,8 @@ impl fmt::Display for Node {
} }
Node::String(s) => s.as_str(), Node::String(s) => s.as_str(),
Node::Block(_) => "<block>", Node::Block(_) => "<block>",
Node::Fn { name, params, body } => "fn",
Node::ReturnStmt(_) => "return",
Node::DepList(_) => "depend", Node::DepList(_) => "depend",
Node::SourceList(_) => "source", Node::SourceList(_) => "source",
Node::Array(_) => "<array>", Node::Array(_) => "<array>",

@ -35,12 +35,14 @@ const fn kw(raw: &'static str, kind: token::Kind) -> KeywordMap {
KeywordMap { raw, kind } KeywordMap { raw, kind }
} }
static KEYWORDS: [KeywordMap; 10] = [ static KEYWORDS: [KeywordMap; 12] = [
kw("depend", token::Kind::DependKeyword), kw("depend", token::Kind::DependKeyword),
kw("else", token::Kind::ElseKeyword), kw("else", token::Kind::ElseKeyword),
kw("false", token::Kind::FalseKeyword), kw("false", token::Kind::FalseKeyword),
kw("fn", token::Kind::FnKeyword),
kw("if", token::Kind::IfKeyword), kw("if", token::Kind::IfKeyword),
kw("include", token::Kind::IncludeKeyword), kw("include", token::Kind::IncludeKeyword),
kw("return", token::Kind::ReturnKeyword),
kw("set", token::Kind::SetKeyword), kw("set", token::Kind::SetKeyword),
kw("source", token::Kind::SourceKeyword), kw("source", token::Kind::SourceKeyword),
kw("target", token::Kind::TargetKeyword), kw("target", token::Kind::TargetKeyword),

@ -71,8 +71,10 @@ pub enum Kind {
DependKeyword, DependKeyword,
ElseKeyword, ElseKeyword,
FalseKeyword, FalseKeyword,
FnKeyword,
IfKeyword, IfKeyword,
IncludeKeyword, IncludeKeyword,
ReturnKeyword,
SetKeyword, SetKeyword,
SourceKeyword, SourceKeyword,
TargetKeyword, TargetKeyword,
@ -98,9 +100,11 @@ impl Kind {
match self { match self {
k if k.is_start_of_lhs_expr() => true, k if k.is_start_of_lhs_expr() => true,
Kind::FalseKeyword => true, Kind::FalseKeyword => true,
Kind::FnKeyword => true,
Kind::Minus => true, Kind::Minus => true,
Kind::OBracket => true, Kind::OBracket => true,
Kind::OParen => true, Kind::OParen => true,
Kind::ReturnKeyword => true,
Kind::TrueKeyword => true, Kind::TrueKeyword => true,
_ => false, _ => false,
} }
@ -214,8 +218,10 @@ impl fmt::Display for Kind {
Kind::DependKeyword => "keyword", Kind::DependKeyword => "keyword",
Kind::ElseKeyword => "keyword", Kind::ElseKeyword => "keyword",
Kind::FalseKeyword => "keyword", Kind::FalseKeyword => "keyword",
Kind::FnKeyword => "keyword",
Kind::IfKeyword => "keyword", Kind::IfKeyword => "keyword",
Kind::IncludeKeyword => "keyword", Kind::IncludeKeyword => "keyword",
Kind::ReturnKeyword => "keyword",
Kind::SetKeyword => "keyword", Kind::SetKeyword => "keyword",
Kind::SourceKeyword => "keyword", Kind::SourceKeyword => "keyword",
Kind::TargetKeyword => "keyword", Kind::TargetKeyword => "keyword",

@ -6,6 +6,13 @@ set CC_EXE = "clang";
set LINK_EXE = "ld.lld"; set LINK_EXE = "ld.lld";
set BUILD_PREFIX = "build"; set BUILD_PREFIX = "build";
# regular function definitions work very much like they do in ECMAScript 5,
# except that the rather verbose "function" keyword is shortened to "fn".
fn hello(p1, p2) {
print("hello, world");
return p1 + p1;
}
# a target is a single component. # a target is a single component.
# targets can depend on other targets. # targets can depend on other targets.
target kern { target kern {
@ -15,6 +22,11 @@ target kern {
print("2 <= 3"); print("2 <= 3");
} }
# functions are first-class citizens; they can be defined as inline
# anonymous functions that you can treat like any other value
fn_with_callback(fn(a, b) { return a + b; });
(fn(a, b) { return a + b; })(1, 2);
owo = 1 + 2 * 3 * 4 - 5; owo = 1 + 2 * 3 * 4 - 5;
# the type keyword defines whether the target is supposed to be # the type keyword defines whether the target is supposed to be

Loading…
Cancel
Save