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/
|
||||
*.opam
|
||||
|
||||
# Mac OS
|
||||
.DS_Store
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
profile = default
|
||||
version = 0.26.2
|
||||
@@ -1,4 +1,6 @@
|
||||
;bin/dune
|
||||
|
||||
(executable
|
||||
(public_name croissant)
|
||||
(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)
|
||||
|
||||
(generate_opam_files true)
|
||||
|
||||
(source
|
||||
(github username/reponame))
|
||||
|
||||
(authors "Author Name")
|
||||
|
||||
(maintainers "Maintainer Name")
|
||||
|
||||
(source (github LucasVbr/croissant))
|
||||
(authors "LucasVbr")
|
||||
(maintainers "LucasVbr")
|
||||
(license LICENSE)
|
||||
|
||||
(documentation https://url/to/documentation)
|
||||
;(documentation https://url/to/documentation)
|
||||
|
||||
(package
|
||||
(name croissant)
|
||||
(synopsis "A short synopsis")
|
||||
(description "A longer description")
|
||||
(depends ocaml dune)
|
||||
(depends ocaml dune alcotest menhir ocamlformat)
|
||||
(tags
|
||||
(topics "to describe" your project)))
|
||||
|
||||
; See the complete stanza docs at https://dune.readthedocs.io/en/stable/dune-files.html#dune-project
|
||||
("Custom programming language" "French")
|
||||
)
|
||||
)
|
||||
(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
|
||||
(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