mirror of
https://github.com/LucasVbr/croissant.git
synced 2026-05-13 17:12:10 +00:00
feat: Support basic expression
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
# Build folder
|
# Build folder
|
||||||
_build/
|
_build/
|
||||||
|
*.opam
|
||||||
|
|
||||||
# Mac OS
|
# Mac OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
profile = default
|
||||||
|
version = 0.26.2
|
||||||
@@ -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
@@ -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
@@ -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
@@ -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 ^ "])"
|
||||||
@@ -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))
|
||||||
|
|||||||
@@ -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) }
|
||||||
@@ -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
@@ -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 ]
|
||||||
|
);
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user