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 () =
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)
(using menhir 3.0)
(name flan)
@ -19,7 +20,7 @@
(name flan)
(synopsis "A short synopsis")
(description "A longer description")
(depends ocaml dune)
(depends ocaml dune menhir)
(tags
(topics "to describe" your project)))

View File

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

View File

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