example parsing string to AST

This commit is contained in:
omniscient 2024-07-09 21:01:55 +10:00
parent a9e2e3d462
commit a9fa510483
11 changed files with 153 additions and 22 deletions

1
.ocamlformat Normal file
View File

@ -0,0 +1 @@
margin=100

View File

@ -1,4 +1,9 @@
open Flan open Flan.Parse
let () = let () =
Example.print_greeting "mate" let source_str = Flan.Examples.let_bind_int in
let lexbuf = Lexing.from_string source_str in
Printf.printf "Convert source \"%s\" ->\n" source_str;
match parse_program lexbuf with
| Ok ast -> print_ast ast
| Error msg -> print_endline ("ERROR: \n" ^ msg)

View File

@ -1,4 +1,5 @@
(lang dune 3.15) (lang dune 3.15)
(using menhir 3.0)
(name flan) (name flan)
@ -19,7 +20,7 @@
(name flan) (name flan)
(synopsis "A short synopsis") (synopsis "A short synopsis")
(description "A longer description") (description "A longer description")
(depends ocaml dune) (depends ocaml dune menhir)
(tags (tags
(topics "to describe" your project))) (topics "to describe" your project)))

View File

@ -12,6 +12,7 @@ bug-reports: "https://github.com/username/reponame/issues"
depends: [ depends: [
"ocaml" "ocaml"
"dune" {>= "3.15"} "dune" {>= "3.15"}
"menhir"
"odoc" {with-doc} "odoc" {with-doc}
] ]
build: [ build: [

View File

@ -1,2 +1,7 @@
(library (library
(name flan)) (name flan))
(menhir
(modules oflan))
(ocamllex olexer)

View File

@ -1 +0,0 @@
let print_greeting name = print_endline ("G'day, " ^ name)

4
lib/examples.ml Normal file
View File

@ -0,0 +1,4 @@
(** Examples of syntax / programs as strings that can be imported and tested *)
let let_bind_int = "let x = 10"
let let_bind_str = "let s = \"hello\" "

42
lib/oflan.mly Normal file
View File

@ -0,0 +1,42 @@
/* Declarations */
%{
open Omniflan.Ast
%}
%token Eof
%token Newline
%token Let
%token False
%token True
%token <string> Ident
%token <int> Int
%token <float> F32
%token Equal
%token LParen
%token RParen
%start <Omniflan.Ast.program> prog
%%
/* Grammar */
expr:
| i = Int; { Int i }
stmt:
| Let; var_name = Ident; Equal; bound_expr = expr
{ Let {
loc = $startpos;
var_name = var_name;
bindee = bound_expr
}
}
toplevel_item:
| stmt = stmt { Stmt stmt }
prog:
| prog = separated_list(Newline, toplevel_item); Eof { prog }

35
lib/olexer.mll Normal file
View File

@ -0,0 +1,35 @@
{
open Lexing
open Oflan
exception SyntaxError of string
let next_line lexbuf =
let pos = lexbuf.lex_curr_p in
lexbuf.lex_curr_p <-
{ pos with pos_bol = lexbuf.lex_curr_pos;
pos_lnum = pos.pos_lnum + 1
}
}
let digit = ['0'-'9']
let digits = digit*
let alpha = ['a'-'z' 'A'-'Z']
let ident = (alpha) (alpha|digit|'_')* (* regex for identifier *)
let whitespace = [' ' '\t']+
let newline = '\r' | '\n' | "\r\n"
let int = digits
rule read =
parse
| whitespace { read lexbuf }
| newline { next_line lexbuf; read lexbuf }
| int { Int (int_of_string (Lexing.lexeme lexbuf))}
| "let" { Let }
| ident { Ident (Lexing.lexeme lexbuf) }
| '=' { Equal }
| '(' { LParen }
| 'R' { RParen }
| eof { Eof }
| _ { raise (SyntaxError ("Unexpected char: " ^ Lexing.lexeme lexbuf)) }

View File

@ -1,22 +1,29 @@
(*
Notes
type binary_opt = For now everything will be split into modules inside this one big file while prototyping.
| Add *)
| Subtract
| Multiply
| Divide
type expr = module Ast = struct
| Let of { name: string; bindee: expr } type loc = Lexing.position
| Binary of { lhs: expr; rhs: expr; operator: binary_opt } type unary_op = Negate
| IfElse of { condition: expr } type binary_op = Add | Subtract | Multiply | Divide
type literal = Int of int
type builtin_type = type expr = Int of int
| I32 (* | Literal of literal *)
| F32 (* | BinaryOp of { lhs: expr; rhs: expr; operator: binary_op } *)
| Bool (* | IfElse of { condition: expr; if_expr: expr; else_expr: expr } *)
| Char
module Examples = struct and stmt =
let let_bind_int = "let x = 10" | Let of { loc : loc; var_name : string; bindee : expr } (* Let binding "let x = 5" *)
let let_bind_str = "let s = \"hello\" " | FuncDecl (* TODO: arguments *)
and toplevel_item = Stmt of stmt
type builtin_type = I32 | F32 | Bool | Char
type program = toplevel_item list
end end
module Typer = struct end
(** This module helps take an untyped AST and produce a typed AST *)

31
lib/parse.ml Normal file
View File

@ -0,0 +1,31 @@
open Lexing
exception SyntaxError of string
(* Prints the line number and character number where the error occurred.*)
let print_error_position lexbuf =
let pos = lexbuf.lex_curr_p in
Printf.sprintf "Line:%d Position:%d" pos.pos_lnum (pos.pos_cnum - pos.pos_bol + 1)
let parse_program lexbuf =
try Ok (Oflan.prog Olexer.read lexbuf) with
| SyntaxError msg ->
let error_msg = Printf.sprintf "%s: %s\n" (print_error_position lexbuf) msg in
Error error_msg
| Oflan.Error ->
let error_msg = Printf.sprintf "%s: syntax error\n" (print_error_position lexbuf) in
Error error_msg
open Omniflan.Ast
let string_of_expr expr = match expr with Int i -> "Int " ^ string_of_int i
let string_of_stmt stmt =
match stmt with
| Let s -> Printf.sprintf "(%d) Let %s = %s" s.loc.pos_lnum s.var_name (string_of_expr s.bindee)
| FuncDecl -> failwith "TODO"
let print_ast prog =
List.iter
(fun toplevel -> match toplevel with Stmt stmt -> print_endline (string_of_stmt stmt))
prog