From ba66fd899acae822eb393fe79e389d7b6e996a62 Mon Sep 17 00:00:00 2001 From: tamdaz Date: Mon, 27 Jan 2025 23:32:54 +0100 Subject: [PATCH 1/3] Add methods to add C functions, enums, structs and unions. --- spec/libc_spec.cr | 189 ++++++++++++++++++++++++++++++++++++++++++++++ src/types/libc.cr | 80 ++++++++++++++++++++ 2 files changed, 269 insertions(+) create mode 100644 spec/libc_spec.cr create mode 100644 src/types/libc.cr diff --git a/spec/libc_spec.cr b/spec/libc_spec.cr new file mode 100644 index 0000000..2d25716 --- /dev/null +++ b/spec/libc_spec.cr @@ -0,0 +1,189 @@ +require "./spec_helper" + +describe Crygen::Types::LibC do + it "creates a C library" do + libc_type = Crygen::Types::LibC.new("C") + libc_type.generate.should eq(<<-CRYSTAL) + lib C + end + CRYSTAL + end + + it "creates a C library with one function" do + libc_type = Crygen::Types::LibC.new("C") + libc_type.add_function("getch", "Int32") + libc_type.generate.should eq(<<-CRYSTAL) + lib C + fun getch : Int32 + end + CRYSTAL + end + + it "creates a C library with one function and a parameter" do + libc_type = Crygen::Types::LibC.new("C") + libc_type.add_function("getch", "Int32", [{"arg", "String"}]) + libc_type.generate.should eq(<<-CRYSTAL) + lib C + fun getch(arg : String) : Int32 + end + CRYSTAL + end + + it "creates a C library with one function and more parameters" do + args = [ + {"arg", "String"}, + {"value", "Int32"} + ] + + libc_type = Crygen::Types::LibC.new("C") + libc_type.add_function("getch", "Int32", args) + libc_type.generate.should eq(<<-CRYSTAL) + lib C + fun getch(arg : String, value : Int32) : Int32 + end + CRYSTAL + end + + it "creates a C library with many functions" do + libc_type = Crygen::Types::LibC.new("C") + libc_type.add_function("getch", "Int32") + libc_type.add_function("time", "Int32") + libc_type.add_function("getpid", "Int32") + libc_type.generate.should eq(<<-CRYSTAL) + lib C + fun getch : Int32 + fun time : Int32 + fun getpid : Int32 + end + CRYSTAL + end + + it "creates a C library with one struct" do + libc_type = Crygen::Types::LibC.new("C") + libc_type.add_struct("TimeZone", [ + {"field_one", "Int32"}, + {"field_two", "Int32"}, + ]) + libc_type.generate.should eq(<<-CRYSTAL) + lib C + struct TimeZone + field_one : Int32 + field_two : Int32 + end + end + CRYSTAL + end + + it "creates a C library with many structs" do + libc_type = Crygen::Types::LibC.new("C") + libc_type.add_struct("TimeZone", [ + {"field_one", "Int32"}, + {"field_two", "Int32"}, + ]) + + libc_type.add_struct("DateTime", [ + {"timestamp", "Int64"}, + ]) + libc_type.generate.should eq(<<-CRYSTAL) + lib C + struct TimeZone + field_one : Int32 + field_two : Int32 + end + + struct DateTime + timestamp : Int64 + end + end + CRYSTAL + end + + it "creates a C library with one union" do + libc_type = Crygen::Types::LibC.new("C") + libc_type.add_union("IntOrFloat", [ + {"some_int", "Int32"}, + {"some_float", "Float64"}, + ]) + libc_type.generate.should eq(<<-CRYSTAL) + lib C + union IntOrFloat + some_int : Int32 + some_float : Float64 + end + end + CRYSTAL + end + + it "creates a C library with many unions" do + libc_type = Crygen::Types::LibC.new("C") + libc_type.add_union("IntOrFloat", [ + {"some_int", "Int32"}, + {"some_float", "Float64"}, + ]) + libc_type.add_union("CharOrString", [ + {"some_char", "Char"}, + {"some_string", "String"}, + ]) + libc_type.generate.should eq(<<-CRYSTAL) + lib C + union IntOrFloat + some_int : Int32 + some_float : Float64 + end + + union CharOrString + some_char : Char + some_string : String + end + end + CRYSTAL + end + + it "creates a C library with functions, structs and unions" do + libc_type = Crygen::Types::LibC.new("C") + libc_type.add_function("getch", "Int32") + libc_type.add_function("time", "Int32") + libc_type.add_function("getpid", "Int32") + libc_type.add_struct("TimeZone", [ + {"field_one", "Int32"}, + {"field_two", "Int32"}, + ]) + libc_type.add_struct("DateTime", [ + {"timestamp", "Int64"}, + ]) + libc_type.add_union("IntOrFloat", [ + {"some_int", "Int32"}, + {"some_float", "Float64"}, + ]) + libc_type.add_union("CharOrString", [ + {"some_char", "Char"}, + {"some_string", "String"}, + ]) + libc_type.generate.should eq(<<-CRYSTAL) + lib C + struct TimeZone + field_one : Int32 + field_two : Int32 + end + + struct DateTime + timestamp : Int64 + end + + union IntOrFloat + some_int : Int32 + some_float : Float64 + end + + union CharOrString + some_char : Char + some_string : String + end + + fun getch : Int32 + fun time : Int32 + fun getpid : Int32 + end + CRYSTAL + end +end diff --git a/src/types/libc.cr b/src/types/libc.cr new file mode 100644 index 0000000..9faeb7b --- /dev/null +++ b/src/types/libc.cr @@ -0,0 +1,80 @@ +require "./../interfaces/generator" +require "./../types/*" + +# A class that generates a C library. +# ``` +# libc_type = Crygen::Types::LibC.new("C") +# libc_type.add_function("getch", "Int32") +# libc_type.generate +# ``` +# Output : +# ``` +# lib C +# fun getch : Int32 +# end +# ``` +class Crygen::Types::LibC < Crygen::Abstract::GeneratorInterface + alias FieldArray = Array(Tuple(String, String)) + + @functions = [] of Hash(Symbol, String) + @objects = [] of Tuple(String, Symbol, FieldArray) + + def initialize(@name : String); end + + # Adds a C function (name and return type). + def add_function(name : String, return_type : String, args : Array(Tuple(String, String)) | Nil = nil) : Nil + @functions << { + :name => name, + :args => !args.nil? ? generate_args(args) : "", + :return_type => return_type, + } + end + + # Adds a struct. + def add_struct(name : String, fields : FieldArray) : Nil + @objects << {name, :struct, fields} + end + + # Adds an union. + def add_union(name : String, fields : FieldArray) : Nil + @objects << {name, :union, fields} + end + + # Generates a C lib. + def generate : String + String.build do |str| + str << "lib #{@name}\n" + can_add_whitespace = false + @objects.each do |object| + str << "\n" if can_add_whitespace == true + str << " #{object[1]} #{object[0]}\n" + object[2].each do |field| + str << " #{field[0]} : #{field[1]}\n" + end + str << " end\n" + can_add_whitespace = true + end + str << "\n" if !@objects.empty? && !@functions.empty? + @functions.each do |function| + if function[:args].empty? + str << " fun #{function[:name]} : #{function[:return_type]}\n" + else + str << " fun #{function[:name]}#{function[:args]} : #{function[:return_type]}\n" + end + end + str << "end" + end + end + + # Generate the args. + private def generate_args(args : Array(Tuple(String, String))) : String + String.build do |str| + str << '(' + args.each_with_index do |arg, i| + str << "#{arg[0]} : #{arg[1]}" + str << ", " if i != args.size - 1 + end + str << ')' + end + end +end From 621edd217c850035e4d7b1c91e4205a9a5d361ba Mon Sep 17 00:00:00 2001 From: tamdaz Date: Mon, 27 Jan 2025 23:50:50 +0100 Subject: [PATCH 2/3] Format spec on C lib. --- spec/libc_spec.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/libc_spec.cr b/spec/libc_spec.cr index 2d25716..a2ac01c 100644 --- a/spec/libc_spec.cr +++ b/spec/libc_spec.cr @@ -32,7 +32,7 @@ describe Crygen::Types::LibC do it "creates a C library with one function and more parameters" do args = [ {"arg", "String"}, - {"value", "Int32"} + {"value", "Int32"}, ] libc_type = Crygen::Types::LibC.new("C") From 9af8d20c8eaec2439698ac692d15bce450724122 Mon Sep 17 00:00:00 2001 From: tamdaz Date: Wed, 29 Jan 2025 19:12:52 +0100 Subject: [PATCH 3/3] Add Lib C-binding section. --- README.md | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b8e3c22..e486e78 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ library : [nette/php-generator](https://github.com/nette/php-generator). - [Todos](#todos) - [Contributing](#contributing) - [Contributors](#contributors) +- [Lib C-binding](#lib-c-binding) ## Installation @@ -330,6 +331,42 @@ end > More examples will be added soon. +### Lib C-binding + +```crystal +libc_type = CGT::LibC.new("C") +libc_type.add_function("getch", "Int32", [{"arg", "Int32"}]) +libc_type.add_function("getpid", "Int32") +libc_type.add_struct("TimeZone", [ + {"field_one", "Int32"}, + {"field_two", "Int32"}, +]) +libc_type.add_union("IntOrFloat", [ + {"some_int", "Int32"}, + {"some_float", "Float64"}, +]) +puts libc_type.generate +``` + +Output : + +```crystal +lib C + struct TimeZone + field_one : Int32 + field_two : Int32 + end + + union IntOrFloat + some_int : Int32 + some_float : Float64 + end + + fun getch(arg : Int32) : Int32 + fun getpid : Int32 +end +``` + ## Usage This library can be used to save time. In particular, the frameworks have features @@ -346,7 +383,7 @@ for generating classes. - [x] : Add a module - [x] : Add a struct - [ ] : Add a macro -- [ ] : Add a lib (C binding) +- [x] : Add a lib (C binding) Once the todos have been checked, this library will be released.