8000 Add Go Bindings generation by nishakm · Pull Request #9 · JPEWdev/shacl2code · GitHub
[go: up one dir, main page]

Skip to content

Add Go Bindings generation #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/shacl2code/lang/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
from .jinja import JinjaRender # noqa: F401
from .python import PythonRender # noqa: F401
from .jsonschema import JsonSchemaRender # noqa: F401
from .golang import GolangRender # noqa: F401
154 changes: 154 additions & 0 deletions src/shacl2code/lang/golang.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#
# Copyright © 2024, Oracle and/or its affiliates
#
# SPDX-License-Identifier: MIT

from .common import BasicJinjaRender
from .lang import language, TEMPLATE_DIR

import re

DATATYPES = {
"http://www.w3.org/2001/XMLSchema#string": "string",
"http://www.w3.org/2001/XMLSchema#anyURI": "string",
"http://www.w3.org/2001/XMLSchema#integer": "int",
"http://www.w3.org/2001/XMLSchema#positiveInteger": "PInt",
"http://www.w3.org/2001/XMLSchema#nonNegativeInteger": "uint",
"http://www.w3.org/2001/XMLSchema#boolean": "bool",
"http://www.w3.org/2001/XMLSchema#decimal": "float64",
"http://www.w3.org/2001/XMLSchema#dateTime": "DateTime",
"http://www.w3.org/2001/XMLSchema#dateTimeStamp": "DateTimeStamp",
}


def upper_first(string):
# upper_first will capitalize the string
return string[0].upper() + string[1:]


def lower_first(string):
# lower_first sets the first character of a string to lower case
return string[0].lower() + string[1:]


def type_name(clsname):
# type_name converts the class name into an exportable go type name
return upper_first(''.join(clsname).replace('_',''))


def interface_method(propname):
# returns an interface method name
# The interface method names are capitalized so they can be exported
parts = propname.split('_')
parts[0] = upper_first(parts[0])
return ''.join(parts)

def struct_name(clsname):
# Go structs are only used when a class is a concrete class
return lower_first(type_name(clsname)) + "Impl"


def struct_prop_name(propname):
# All properties in a Go struct are non-exportable
return propname.replace('_','')


def setter_prop_type(prop):
typ = prop_type(prop)
return typ.replace('[]', "...")


def struct_prop(prop):
return struct_prop_name(prop) + ' ' + prop_type(prop)

< 10000 /span>

def interface_getter(prop):
return type_name(prop.varname) + '() ' + prop_type(prop)


def interface_setter(prop):
return "Set" + type_name(prop.varname) + '(' + setter_prop_type(prop) + ') error'


def struct_getter(cls, prop):
return 'func (o *' + struct_name(cls) + ') ' + type_name(prop.varname) + '() ' + prop_type(prop) + '{\n' \
+ ' return o.' + struct_prop_name(prop) + '\n' \
+ '}'


def struct_setter(cls, prop):
return 'func (o *' + struct_name(cls) + ') Set' + type_name(prop.varname) + '(v ' + setter_prop_type(prop) + ') error{\n' \
+ ' o.' + struct_prop_name(prop) + ' = v\n' \
+ ' return nil\n' \
+ '}'


def comment(indent_with, identifier, text):
if text.lower().startswith(identifier.lower()):
text = identifier + " " + text[len(identifier):]

return indent(indent_with, text)

def parent_has_prop(classes, cls, prop):
for parent_id in cls.parent_ids:
parent = classes.get(parent_id)
for p in parent.properties:
if p.varname == prop.varname:
return True
if parent_has_prop(classes, parent, prop):
return True

return False


def include_prop(classes, cls, prop):
return not parent_has_prop(classes, cls, prop)




def interface_name(cls):
return type_name(cls.clsname)




def indent(indent_with, str):
parts = re.split("\n", str)
return indent_with + ("\n"+indent_with).join(parts)


@language("golang")
class GolangRender(BasicJinjaRender):
HELP = "Go Language Bindings"

def __init__(self, args):
super().__init__(args, TEMPLATE_DIR / "golang.j2")
self.__render_args = {
"module": args.module,
}

@classmethod
def get_arguments(cls, parser):
super().get_arguments(parser)
parser.add_argument("--module", help="Go module name")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably want required=True to make this option required, since the option is required to generate correct bindings


def get_extra_env(self):
return {
"type_name": type_name,
"interface_method": interface_method,
"struct_prop_name": struct_prop_name,
"struct_name": struct_name,
"struct_getter": struct_getter,
"struct_setter": struct_setter,
"interface_name": interface_name,
"interface_getter": interface_getter,
"interface_setter": interface_setter,
"include_prop": include_prop,
"indent": indent,
"comment": comment,
"DATATYPES": DATATYPES,
}

def get_additional_render_args(self):
return self.__render_args
119 changes: 119 additions & 0 deletions src/shacl2code/lang/templates/golang.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// package spdx_v3_0 provides structs for SPDX 3.0
//
// {{ disclaimer }}
//
// SPDX-License-Identifier: MIT

{# helpful macros #}

{%- macro array(prop) %}
{%- if prop.max_count and prop.max_count >= 1 -%}
[]
{%- endif -%}
{%- endmacro %}

{%- macro many(prop) %}
{%- if prop.max_count and prop.max_count >= 1 -%}
...
{%- endif -%}
{%- endmacro %}

{%- macro prop_type(prop) %}
{%- if prop.class_id -%}
{{ type_name(classes.get(prop.class_id).clsname) }}
{%- else -%}
{{ DATATYPES[prop.datatype] }}
{%- endif %}
{%- endmacro %}

{%- macro namedind(class) %}
type {{ type_name(class.clsname) }} struct {
ObjRef {{ type_name(class.clsname) }}Ref
}
{%- endmacro %}

{%- macro interface(class) %}
type {{ type_name(class.clsname) }} interface {
{% for parent in class.parent_ids %}
{{ type_name(classes.get(parent).clsname) }}
{% endfor %}
{% for prop in class.properties %}
{{ interface_method(prop.varname) }}() {{ array(prop) }}{{ prop_type(prop) }}
Set{{ interface_method(prop.varname) }}(p {{ many(prop) }}{{ prop_type(prop) }}) error
{% endfor %}
}
{%- endmacro %}

{%- macro struct(class) %}
type {{ struct_name(class.clsname) }} struct {
{% for parent in class.parent_ids %}
//-----------{{ type_name(classes.get(parent).clsname) }}--------
{% for prop in classes.get(parent).properties %}
{{ struct_prop_name(prop.varname) }} {{ array(prop) }}{{ prop_type(prop) }}
{% endfor %}
{% endfor %}
{% if not class.named_individuals %}
{% for prop in class.properties %}
{{ struct_prop_name(prop.varname) }} {{ array(prop) }}{{ prop_type(prop) }}
{% endfor %}
{% endif %}
}
{%- endmacro %}

{%- macro constructor(class) %}
func New{{ type_name(class.clsname) }}() {{ type_name(class.clsname) }} {
return &{{ struct_name(class.clsname) }}{}
}
{%- endmacro %}

{%- macro func(class) %}
{%- for prop in class.properties %}
func (o * {{ type_name(class.clsname) }}) {{ interface_method(prop.varname) }}() {{ prop_type(prop) }}{
return o.{{ struct_prop_name(prop.varname) }}
}

func (o *{{ type_name(class.clsname) }}) Set{{ interface_method(prop.varname) }}(v {{ prop_type(prop) }}) error {
o.{{ struct_prop_name(prop.varname) }} = v
return nil
}
{%- endfor %}
{%- endmacro %}

{# go file content #}
package spdx_v{{ module }}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

package {{ module }}

Remember that shacl2code is not (intended) to be specific to SPDX :)


// NAMED INDIVIDUALS

{%- for class in classes %}
{%- if class.named_individuals %}
type {{ type_name(class.clsname) }}Ref string
var (
{%- for ind in class.named_individuals %}
{{ type_name(class.clsname) }}{{ ind.varname }} {{ type_name(class.clsname) }}Ref = "{{ ind._id }}"
{%- endfor %}
)
{%- endif %}
{%- endfor %}

// INTERFACES

{% for class in classes %}
{% if class.named_individuals %}
{{ namedind(class) }}
{% else %}
{{ interface(class) }}
{% endif %}
{% endfor %}

// STRUCTS AND CONSTRUCTORS

{% for class in concrete_classes %}
{{ struct(class) }}
{{ constructor(class) }}
{% endfor %}

// IMPLEMENTATIONS

{% for class in concrete_classes %}
{{ func(class) }}
{% endfor %}
0