feat: Support basic expression

This commit is contained in:
Lucàs
2024-07-02 15:33:58 +02:00
parent c91951308f
commit e563bce4f5
12 changed files with 231 additions and 25 deletions
+1
View File
@@ -5,6 +5,7 @@
# Build folder # Build folder
_build/ _build/
*.opam
# Mac OS # Mac OS
.DS_Store .DS_Store
+2
View File
@@ -0,0 +1,2 @@
profile = default
version = 0.26.2
+4 -2
View File
@@ -1,4 +1,6 @@
;bin/dune
(executable (executable
(public_name croissant)
(name main) (name main)
(libraries croissant)) (public_name croissant)
(libraries parser lexer ast))
+19 -1
View File
@@ -1 +1,19 @@
let () = print_endline "Hello, World!" (* bin/main.ml *)
open Printf
open Ast
let () =
let lexbuf = Lexing.from_channel stdin in
let res =
try Parser.main Lexer.token lexbuf with
| Lexer.Error c ->
fprintf stderr "Lexical error at line %d: Unknown character '%c'\n"
lexbuf.lex_curr_p.pos_lnum c;
exit 1
| Parser.Error ->
fprintf stderr "Parse error at line %d:\n" lexbuf.lex_curr_p.pos_lnum;
exit 1
in
let _ = res in
Printf.printf "%s\n" (string_of_source_file res)
+10 -16
View File
@@ -1,26 +1,20 @@
(lang dune 3.10) (lang dune 3.4)
(name croissant) (name croissant)
(generate_opam_files true) (generate_opam_files true)
(source (github LucasVbr/croissant))
(source (authors "LucasVbr")
(github username/reponame)) (maintainers "LucasVbr")
(authors "Author Name")
(maintainers "Maintainer Name")
(license LICENSE) (license LICENSE)
;(documentation https://url/to/documentation)
(documentation https://url/to/documentation)
(package (package
(name croissant) (name croissant)
(synopsis "A short synopsis") (synopsis "A short synopsis")
(description "A longer description") (description "A longer description")
(depends ocaml dune) (depends ocaml dune alcotest menhir ocamlformat)
(tags (tags
(topics "to describe" your project))) ("Custom programming language" "French")
)
; See the complete stanza docs at https://dune.readthedocs.io/en/stable/dune-files.html#dune-project )
(using menhir 2.1)
+34
View File
@@ -0,0 +1,34 @@
(* lib/ast.ml *)
type binary_operator = Add | Substract | Multiply | Divide
type expression =
| IntegerLiteral of int
| BinaryExpression of binary_operator * expression * expression
type statement = ExpressionStatement of expression
type source_file = SourceFile of statement list
(* Print AST *)
let string_of_binary_operator = function
| Add -> "Add"
| Substract -> "Substract"
| Multiply -> "Multiply"
| Divide -> "Divide"
let rec string_of_expression = function
| IntegerLiteral i -> "IntegerLiteral(" ^ string_of_int i ^ ")"
| BinaryExpression (op, e1, e2) ->
"BinaryExpression("
^ string_of_binary_operator op
^ ", " ^ string_of_expression e1 ^ ", " ^ string_of_expression e2 ^ ")"
let string_of_statement = function
| ExpressionStatement e ->
"ExpressionStatement(" ^ string_of_expression e ^ ")"
let string_of_source_file = function
| SourceFile stmts ->
let stmt_strings = List.map string_of_statement stmts in
"SourceFile([" ^ String.concat ", " stmt_strings ^ "])"
+20 -1
View File
@@ -1,2 +1,21 @@
;lib/dune
(library (library
(name croissant)) (name lexer)
(modules lexer)
(libraries parser))
(library
(name parser)
(modules parser)
(libraries ast))
(library
(name ast)
(modules ast))
(menhir
(modules parser))
(ocamllex
(modules lexer))
+28
View File
@@ -0,0 +1,28 @@
(* lib/lexer.mll *)
{
open Parser
exception Error of char
}
let line_comment = "//" [^ '\n']*
let digit = ['0'-'9']
let integer = digit+
rule token = parse
| [' ' '\t'] | line_comment { token lexbuf }
| ['\n'] { Lexing.new_line lexbuf; token lexbuf }
| '+' { PLUS }
| '-' { MINUS }
| '*' { TIMES }
| '/' { DIVIDE }
| ';' { SEMICOLON }
| integer as lxm { INT(int_of_string lxm) }
| '(' { LPAREN }
| ')' { RPAREN }
| eof { EOF }
| _ as c { raise (Error c) }
+51
View File
@@ -0,0 +1,51 @@
/* lib/parser.mly */
%{
open Ast
%}
%token <int> INT
%token PLUS "+"
%token MINUS "-"
%token TIMES "*"
%token DIVIDE "/"
%token LPAREN "("
%token RPAREN ")"
%token SEMICOLON ";"
%token EOF
%left "+" "-"
%left "*" "/"
//%nonassoc UMINUS
%start main
%type <source_file> main
%%
main:
| statements EOF { SourceFile($1) }
statements:
| statement ";" { [$1] }
| statement ";" statements { $1 :: $3 }
statement:
| expression { ExpressionStatement($1) }
expression:
| literal { $1 }
| binary_expression { $1 }
| "(" expression ")" { $2 }
literal:
| INT { IntegerLiteral($1) }
binary_expression:
| e1=expression PLUS e2=expression { BinaryExpression(Add, e1, e2) }
| e1=expression MINUS e2=expression { BinaryExpression(Substract, e1, e2) }
| e1=expression TIMES e2=expression { BinaryExpression(Multiply, e1, e2) }
| e1=expression DIVIDE e2=expression { BinaryExpression(Divide, e1, e2) }
+54
View File
@@ -0,0 +1,54 @@
(* test/ast.ml *)
open Alcotest
open Ast
let test_string_of_binary_operator () =
check string "+" "Add" (string_of_binary_operator Add);
check string "-" "Substract" (string_of_binary_operator Substract);
check string "*" "Multiply" (string_of_binary_operator Multiply);
check string "/" "Divide" (string_of_binary_operator Divide)
let test_string_of_expression () =
let expr = BinaryExpression (Add, IntegerLiteral 1, IntegerLiteral 2) in
check string "1 + 2"
"BinaryExpression(Add, IntegerLiteral(1), IntegerLiteral(2))"
(string_of_expression expr)
let test_string_of_statement () =
let stmt = ExpressionStatement (IntegerLiteral 42) in
check string "42;" "ExpressionStatement(IntegerLiteral(42))"
(string_of_statement stmt)
let test_string_of_source_file () =
let source_file =
SourceFile
[
ExpressionStatement (IntegerLiteral 1);
ExpressionStatement
(BinaryExpression (Add, IntegerLiteral 2, IntegerLiteral 3));
]
in
check string "1; 2 + 3;"
"SourceFile([ExpressionStatement(IntegerLiteral(1)), \
ExpressionStatement(BinaryExpression(Add, IntegerLiteral(2), \
IntegerLiteral(3)))])"
(string_of_source_file source_file)
let () =
let open Alcotest in
run "AST tests"
[
( "string_of_binary_operator",
[
test_case "string_of_binary_operator" `Quick
test_string_of_binary_operator;
] );
( "string_of_expression",
[ test_case "string_of_expression" `Quick test_string_of_expression ] );
( "string_of_statement",
[ test_case "string_of_statement" `Quick test_string_of_statement ] );
( "string_of_source_file",
[ test_case "string_of_source_file" `Quick test_string_of_source_file ]
);
]
View File
+4 -1
View File
@@ -1,2 +1,5 @@
;test/dune
(test (test
(name croissant)) (name ast)
(libraries ast alcotest))