(*===---------------------------------------------------------------------=== * Parser *===---------------------------------------------------------------------===*) (* binop_precedence - This holds the precedence for each binary operator that is * defined *) let binop_precedence:(char, int) Hashtbl.t = Hashtbl.create 10 (* precedence - Get the precedence of the pending binary operator token. *) let precedence c = try Hashtbl.find binop_precedence c with Not_found -> -1 (* primary * ::= identifier * ::= numberexpr * ::= parenexpr * ::= ifexpr * ::= forexpr * ::= varexpr *) let rec parse_primary = parser (* numberexpr ::= number *) | [< 'Token.Number n >] -> Ast.Number n (* parenexpr ::= '(' expression ')' *) | [< 'Token.Kwd '('; e=parse_expr; 'Token.Kwd ')' ?? "expected ')'" >] -> e (* identifierexpr * ::= identifier * ::= identifier '(' argumentexpr ')' *) | [< 'Token.Ident id; stream >] -> let rec parse_args accumulator = parser | [< e=parse_expr; stream >] -> begin parser | [< 'Token.Kwd ','; e=parse_args (e :: accumulator) >] -> e | [< >] -> e :: accumulator end stream | [< >] -> accumulator in let rec parse_ident id = parser (* Call. *) | [< 'Token.Kwd '('; args=parse_args []; 'Token.Kwd ')' ?? "expected ')'">] -> Ast.Call (id, Array.of_list (List.rev args)) (* Simple variable ref. *) | [< >] -> Ast.Variable id in parse_ident id stream (* ifexpr ::= 'if' expr 'then' expr 'else' expr *) | [< 'Token.If; c=parse_expr; 'Token.Then ?? "expected 'then'"; t=parse_expr; 'Token.Else ?? "expected 'else'"; e=parse_expr >] -> Ast.If (c, t, e) (* forexpr ::= 'for' identifier '=' expr ',' expr (',' expr)? 'in' expression *) | [< 'Token.For; 'Token.Ident id ?? "expected identifier after for"; 'Token.Kwd '=' ?? "expected '=' after for"; stream >] -> begin parser | [< start=parse_expr; 'Token.Kwd ',' ?? "expected ',' after for"; end_=parse_expr; stream >] -> let step = begin parser | [< 'Token.Kwd ','; step=parse_expr >] -> Some step | [< >] -> None end stream in begin parser | [< 'Token.In; body=parse_expr >] -> Ast.For (id, start, end_, step, body) | [< >] -> raise (Stream.Error "expected 'in' after for") end stream | [< >] -> raise (Stream.Error "expected '=' after for") end stream (* varexpr * ::= 'var' identifier ('=' expression? * (',' identifier ('=' expression)?)* 'in' expression *) | [< 'Token.Var; (* At least one variable name is required. *) 'Token.Ident id ?? "expected identifier after var"; init=parse_var_init; var_names=parse_var_names [(id, init)]; (* At this point, we have to have 'in'. *) 'Token.In ?? "expected 'in' keyword after 'var'"; body=parse_expr >] -> Ast.Var (Array.of_list (List.rev var_names), body) | [< >] -> raise (Stream.Error "unknown token when expecting an expression.") (* unary * ::= primary * ::= '!' unary *) and parse_unary = parser (* If this is a unary operator, read it. *) | [< 'Token.Kwd op when op != '(' && op != ')'; operand=parse_expr >] -> Ast.Unary (op, operand) (* If the current token is not an operator, it must be a primary expr. *) | [< stream >] -> parse_primary stream (* binoprhs * ::= ('+' primary)* *) and parse_bin_rhs expr_prec lhs stream = match Stream.peek stream with (* If this is a binop, find its precedence. *) | Some (Token.Kwd c) when Hashtbl.mem binop_precedence c -> let token_prec = precedence c in (* If this is a binop that binds at least as tightly as the current binop, * consume it, otherwise we are done. *) if token_prec < expr_prec then lhs else begin (* Eat the binop. *) Stream.junk stream; (* Parse the primary expression after the binary operator. *) let rhs = parse_unary stream in (* Okay, we know this is a binop. *) let rhs = match Stream.peek stream with | Some (Token.Kwd c2) -> (* If BinOp binds less tightly with rhs than the operator after * rhs, let the pending operator take rhs as its lhs. *) let next_prec = precedence c2 in if token_prec < next_prec then parse_bin_rhs (token_prec + 1) rhs stream else rhs | _ -> rhs in (* Merge lhs/rhs. *) let lhs = Ast.Binary (c, lhs, rhs) in parse_bin_rhs expr_prec lhs stream end | _ -> lhs and parse_var_init = parser (* read in the optional initializer. *) | [< 'Token.Kwd '='; e=parse_expr >] -> Some e | [< >] -> None and parse_var_names accumulator = parser | [< 'Token.Kwd ','; 'Token.Ident id ?? "expected identifier list after var"; init=parse_var_init; e=parse_var_names ((id, init) :: accumulator) >] -> e | [< >] -> accumulator (* expression * ::= primary binoprhs *) and parse_expr = parser | [< lhs=parse_unary; stream >] -> parse_bin_rhs 0 lhs stream (* prototype * ::= id '(' id* ')' * ::= binary LETTER number? (id, id) * ::= unary LETTER number? (id) *) let parse_prototype = let rec parse_args accumulator = parser | [< 'Token.Ident id; e=parse_args (id::accumulator) >] -> e | [< >] -> accumulator in let parse_operator = parser | [< 'Token.Unary >] -> "unary", 1 | [< 'Token.Binary >] -> "binary", 2 in let parse_binary_precedence = parser | [< 'Token.Number n >] -> int_of_float n | [< >] -> 30 in parser | [< 'Token.Ident id; 'Token.Kwd '(' ?? "expected '(' in prototype"; args=parse_args []; 'Token.Kwd ')' ?? "expected ')' in prototype" >] -> (* success. *) Ast.Prototype (id, Array.of_list (List.rev args)) | [< (prefix, kind)=parse_operator; 'Token.Kwd op ?? "expected an operator"; (* Read the precedence if present. *) binary_precedence=parse_binary_precedence; 'Token.Kwd '(' ?? "expected '(' in prototype"; args=parse_args []; 'Token.Kwd ')' ?? "expected ')' in prototype" >] -> let name = prefix ^ (String.make 1 op) in let args = Array.of_list (List.rev args) in (* Verify right number of arguments for operator. *) if Array.length args != kind then raise (Stream.Error "invalid number of operands for operator") else if kind == 1 then Ast.Prototype (name, args) else Ast.BinOpPrototype (name, args, binary_precedence) | [< >] -> raise (Stream.Error "expected function name in prototype") (* definition ::= 'def' prototype expression *) let parse_definition = parser | [< 'Token.Def; p=parse_prototype; e=parse_expr >] -> Ast.Function (p, e) (* toplevelexpr ::= expression *) let parse_toplevel = parser | [< e=parse_expr >] -> (* Make an anonymous proto. *) Ast.Function (Ast.Prototype ("", [||]), e) (* external ::= 'extern' prototype *) let parse_extern = parser | [< 'Token.Extern; e=parse_prototype >] -> e