Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
9b209dfe63 | |||
e9a422ef07 |
157
examples/langV1/debug.go
Normal file
157
examples/langV1/debug.go
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"masonry/lang"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Read the example.masonry file
|
||||||
|
content, err := ioutil.ReadFile("example.masonry")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error reading example.masonry: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
input := string(content)
|
||||||
|
|
||||||
|
ast, err := lang.ParseInput(input)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error: %v\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("🎉 Successfully parsed complete DSL with pages!\n\n")
|
||||||
|
|
||||||
|
for _, def := range ast.Definitions {
|
||||||
|
if def.Server != nil {
|
||||||
|
fmt.Printf("📡 Server: %s\n", def.Server.Name)
|
||||||
|
for _, setting := range def.Server.Settings {
|
||||||
|
if setting.Host != nil {
|
||||||
|
fmt.Printf(" host: %s\n", *setting.Host)
|
||||||
|
}
|
||||||
|
if setting.Port != nil {
|
||||||
|
fmt.Printf(" port: %d\n", *setting.Port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
if def.Entity != nil {
|
||||||
|
entity := def.Entity
|
||||||
|
fmt.Printf("🏗️ Entity: %s", entity.Name)
|
||||||
|
if entity.Description != nil {
|
||||||
|
fmt.Printf(" - %s", *entity.Description)
|
||||||
|
}
|
||||||
|
fmt.Printf("\n")
|
||||||
|
|
||||||
|
for _, field := range entity.Fields {
|
||||||
|
fmt.Printf(" %s: %s", field.Name, field.Type)
|
||||||
|
if field.Required {
|
||||||
|
fmt.Printf(" (required)")
|
||||||
|
}
|
||||||
|
if field.Unique {
|
||||||
|
fmt.Printf(" (unique)")
|
||||||
|
}
|
||||||
|
if field.Default != nil {
|
||||||
|
fmt.Printf(" default=%s", *field.Default)
|
||||||
|
}
|
||||||
|
fmt.Printf("\n")
|
||||||
|
}
|
||||||
|
fmt.Printf("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
if def.Endpoint != nil {
|
||||||
|
endpoint := def.Endpoint
|
||||||
|
fmt.Printf("🚀 Endpoint: %s %s", endpoint.Method, endpoint.Path)
|
||||||
|
if endpoint.Entity != nil {
|
||||||
|
fmt.Printf(" (for %s)", *endpoint.Entity)
|
||||||
|
}
|
||||||
|
if endpoint.Description != nil {
|
||||||
|
fmt.Printf(" - %s", *endpoint.Description)
|
||||||
|
}
|
||||||
|
if endpoint.Auth {
|
||||||
|
fmt.Printf(" [AUTH]")
|
||||||
|
}
|
||||||
|
fmt.Printf("\n\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
if def.Page != nil {
|
||||||
|
page := def.Page
|
||||||
|
fmt.Printf("🎨 Page: %s at %s", page.Name, page.Path)
|
||||||
|
if page.Title != nil {
|
||||||
|
fmt.Printf(" - %s", *page.Title)
|
||||||
|
}
|
||||||
|
if page.Auth {
|
||||||
|
fmt.Printf(" [AUTH]")
|
||||||
|
}
|
||||||
|
fmt.Printf("\n")
|
||||||
|
fmt.Printf(" Layout: %s\n", page.Layout)
|
||||||
|
|
||||||
|
for _, meta := range page.Meta {
|
||||||
|
fmt.Printf(" Meta %s: %s\n", meta.Name, meta.Content)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, comp := range page.Components {
|
||||||
|
fmt.Printf(" 📦 Component: %s", comp.Type)
|
||||||
|
if comp.Entity != nil {
|
||||||
|
fmt.Printf(" for %s", *comp.Entity)
|
||||||
|
}
|
||||||
|
fmt.Printf("\n")
|
||||||
|
|
||||||
|
for _, attr := range comp.Config {
|
||||||
|
if attr.Fields != nil {
|
||||||
|
fmt.Printf(" fields: %v\n", attr.Fields.Fields)
|
||||||
|
}
|
||||||
|
if attr.Actions != nil {
|
||||||
|
fmt.Printf(" actions: ")
|
||||||
|
for i, action := range attr.Actions.Actions {
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Printf(", ")
|
||||||
|
}
|
||||||
|
fmt.Printf("%s", action.Name)
|
||||||
|
if action.Endpoint != nil {
|
||||||
|
fmt.Printf(" via %s", *action.Endpoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf("\n")
|
||||||
|
}
|
||||||
|
if attr.DataSource != nil {
|
||||||
|
fmt.Printf(" data from: %s\n", attr.DataSource.Endpoint)
|
||||||
|
}
|
||||||
|
if attr.Style != nil {
|
||||||
|
fmt.Printf(" style: %s", *attr.Style.Theme)
|
||||||
|
if len(attr.Style.Classes) > 0 {
|
||||||
|
fmt.Printf(" classes: %v", attr.Style.Classes)
|
||||||
|
}
|
||||||
|
fmt.Printf("\n")
|
||||||
|
}
|
||||||
|
if attr.Pagination != nil {
|
||||||
|
fmt.Printf(" pagination: enabled")
|
||||||
|
if attr.Pagination.PageSize != nil {
|
||||||
|
fmt.Printf(" size %d", *attr.Pagination.PageSize)
|
||||||
|
}
|
||||||
|
fmt.Printf("\n")
|
||||||
|
}
|
||||||
|
if attr.Filters != nil {
|
||||||
|
fmt.Printf(" filters: ")
|
||||||
|
for i, filter := range attr.Filters.Filters {
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Printf(", ")
|
||||||
|
}
|
||||||
|
fmt.Printf("%s as %s", filter.Field, filter.Type)
|
||||||
|
if filter.Label != nil {
|
||||||
|
fmt.Printf(" (%s)", *filter.Label)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf("\n")
|
||||||
|
}
|
||||||
|
if attr.Validation {
|
||||||
|
fmt.Printf(" validation: enabled\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
125
examples/langV1/example.masonry
Normal file
125
examples/langV1/example.masonry
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
// Example Masonry DSL definition
|
||||||
|
// This demonstrates the comprehensive language structure
|
||||||
|
|
||||||
|
// Server configuration
|
||||||
|
server MyApp host "localhost" port 8080
|
||||||
|
|
||||||
|
// Entity definitions with various field types and relationships
|
||||||
|
entity User desc "User account management"
|
||||||
|
id: uuid required unique
|
||||||
|
email: string required validate email validate min_length "5"
|
||||||
|
name: string default "Anonymous"
|
||||||
|
created_at: timestamp default "now()"
|
||||||
|
profile_id: uuid relates to Profile as one via "user_id"
|
||||||
|
|
||||||
|
entity Profile desc "User profile information"
|
||||||
|
id: uuid required unique
|
||||||
|
user_id: uuid required relates to User as one
|
||||||
|
bio: text validate max_length "500"
|
||||||
|
avatar_url: string validate url
|
||||||
|
updated_at: timestamp
|
||||||
|
posts: uuid relates to Post as many
|
||||||
|
|
||||||
|
entity Post desc "Blog posts"
|
||||||
|
id: uuid required unique
|
||||||
|
title: string required validate min_length "1" validate max_length "200"
|
||||||
|
content: text required
|
||||||
|
author_id: uuid required relates to User as one
|
||||||
|
published: boolean default "false"
|
||||||
|
created_at: timestamp default "now()"
|
||||||
|
tags: uuid relates to Tag as many through "post_tags"
|
||||||
|
|
||||||
|
entity Tag desc "Content tags"
|
||||||
|
id: uuid required unique
|
||||||
|
name: string required unique validate min_length "1" validate max_length "50"
|
||||||
|
slug: string required unique indexed
|
||||||
|
created_at: timestamp default "now()"
|
||||||
|
|
||||||
|
// API Endpoints with different HTTP methods and parameter sources
|
||||||
|
endpoint GET "/users" for User desc "List users" auth
|
||||||
|
param page: int from query
|
||||||
|
param limit: int required from query
|
||||||
|
returns list as "json" fields [id, email, name]
|
||||||
|
|
||||||
|
endpoint POST "/users" for User desc "Create user"
|
||||||
|
param user_data: object required from body
|
||||||
|
returns object as "json" fields [id, email, name]
|
||||||
|
|
||||||
|
endpoint PUT "/users/{id}" for User desc "Update user"
|
||||||
|
param id: uuid required from path
|
||||||
|
param user_data: object required from body
|
||||||
|
returns object
|
||||||
|
custom "update_user_logic"
|
||||||
|
|
||||||
|
endpoint DELETE "/users/{id}" for User desc "Delete user" auth
|
||||||
|
param id: uuid required from path
|
||||||
|
returns object
|
||||||
|
|
||||||
|
endpoint GET "/posts" for Post desc "List posts"
|
||||||
|
param author_id: uuid from query
|
||||||
|
param published: boolean from query
|
||||||
|
param page: int from query
|
||||||
|
returns list as "json" fields [id, title, author_id, published]
|
||||||
|
|
||||||
|
endpoint POST "/posts" for Post desc "Create post" auth
|
||||||
|
param post_data: object required from body
|
||||||
|
returns object fields [id, title, content, author_id]
|
||||||
|
|
||||||
|
// Frontend pages with components
|
||||||
|
page UserManagement at "/admin/users" layout AdminLayout title "User Management" auth
|
||||||
|
meta description "Manage system users"
|
||||||
|
meta keywords "users, admin, management"
|
||||||
|
|
||||||
|
component Table for User
|
||||||
|
fields [email, name, id]
|
||||||
|
actions [edit via "/users/{id}", delete via "/users/{id}", create via "/users"]
|
||||||
|
data from "/users"
|
||||||
|
style modern classes ["table-striped", "table-hover"]
|
||||||
|
pagination size 20
|
||||||
|
filters [email as text label "Search email", name as text label "Search name"]
|
||||||
|
validate
|
||||||
|
|
||||||
|
component Form for User
|
||||||
|
fields [email, name]
|
||||||
|
actions [save via "/users", cancel]
|
||||||
|
style clean
|
||||||
|
validate
|
||||||
|
|
||||||
|
page UserList at "/users" layout MainLayout title "Users"
|
||||||
|
meta description "Browse all users"
|
||||||
|
|
||||||
|
component Table for User
|
||||||
|
fields [email, name]
|
||||||
|
data from "/users"
|
||||||
|
pagination size 10
|
||||||
|
filters [name as text label "Search by name"]
|
||||||
|
|
||||||
|
page PostManagement at "/admin/posts" layout AdminLayout title "Post Management" auth
|
||||||
|
meta description "Manage blog posts"
|
||||||
|
meta keywords "posts, blog, content"
|
||||||
|
|
||||||
|
component Table for Post
|
||||||
|
fields [title, author_id, published, created_at]
|
||||||
|
actions [edit via "/posts/{id}", delete via "/posts/{id}", create via "/posts"]
|
||||||
|
data from "/posts"
|
||||||
|
style modern
|
||||||
|
pagination size 15
|
||||||
|
filters [title as text label "Search title", published as select label "Published status"]
|
||||||
|
validate
|
||||||
|
|
||||||
|
page CreatePost at "/posts/new" layout MainLayout title "Create Post" auth
|
||||||
|
component Form for Post
|
||||||
|
fields [title, content]
|
||||||
|
actions [save via "/posts", cancel]
|
||||||
|
style clean
|
||||||
|
validate
|
||||||
|
|
||||||
|
page BlogList at "/blog" layout PublicLayout title "Blog Posts"
|
||||||
|
meta description "Read our latest blog posts"
|
||||||
|
meta keywords "blog, articles, content"
|
||||||
|
|
||||||
|
component Table for Post
|
||||||
|
fields [title, created_at]
|
||||||
|
data from "/posts"
|
||||||
|
pagination size 5
|
||||||
|
filters [title as text label "Search posts"]
|
33
examples/langV2/debug.go
Normal file
33
examples/langV2/debug.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"masonry/langV2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Read the example.masonry file
|
||||||
|
content, err := ioutil.ReadFile("example.masonry2")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error reading example.masonry: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
input := string(content)
|
||||||
|
|
||||||
|
ast, err := langV2.ParseInput(input)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error: %v\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("🎉 Successfully parsed complete DSL with pages!\n\n")
|
||||||
|
|
||||||
|
out, err := json.MarshalIndent(ast, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
println(string(out))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
39
examples/langV2/example.masonry2
Normal file
39
examples/langV2/example.masonry2
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
entity User {
|
||||||
|
id: string required unique
|
||||||
|
email: string required unique validate email
|
||||||
|
name: string required
|
||||||
|
age: int validate min "18"
|
||||||
|
created_at: datetime
|
||||||
|
}
|
||||||
|
|
||||||
|
entity Post {
|
||||||
|
id: string required unique
|
||||||
|
title: string required validate maxlen "100"
|
||||||
|
content: string required
|
||||||
|
author_id: string required relates to User as one via "user_id"
|
||||||
|
published: bool default "false"
|
||||||
|
}
|
||||||
|
|
||||||
|
server UserService {
|
||||||
|
GET "/users" entity User
|
||||||
|
POST "/users" entity User
|
||||||
|
GET "/users/{id}" entity User
|
||||||
|
PUT "/users/{id}" entity User
|
||||||
|
}
|
||||||
|
|
||||||
|
server BlogService {
|
||||||
|
GET "/posts" entity Post
|
||||||
|
POST "/posts" entity Post
|
||||||
|
GET "/posts/{id}" entity Post
|
||||||
|
DELETE "/posts/{id}" entity Post
|
||||||
|
}
|
||||||
|
|
||||||
|
page UserProfile {
|
||||||
|
User from UserService
|
||||||
|
Post from BlogService
|
||||||
|
}
|
||||||
|
|
||||||
|
page Dashboard {
|
||||||
|
User from UserService
|
||||||
|
Post from BlogService
|
||||||
|
}
|
136
examples/sdk/debug_sdk.go
Normal file
136
examples/sdk/debug_sdk.go
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"masonry/sdk"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
userEntity := sdk.Entity{
|
||||||
|
Name: "User",
|
||||||
|
Description: nil,
|
||||||
|
Fields: []sdk.Field{
|
||||||
|
{
|
||||||
|
Name: "id",
|
||||||
|
Type: "int",
|
||||||
|
Required: true,
|
||||||
|
Unique: true,
|
||||||
|
Default: nil,
|
||||||
|
Validations: []sdk.Validation{
|
||||||
|
{
|
||||||
|
Type: "validate",
|
||||||
|
Value: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Relationship: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "username",
|
||||||
|
Type: "string",
|
||||||
|
Required: true,
|
||||||
|
Unique: true,
|
||||||
|
Default: nil,
|
||||||
|
Validations: []sdk.Validation{
|
||||||
|
{
|
||||||
|
Type: "validate",
|
||||||
|
Value: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Relationship: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "email",
|
||||||
|
Type: "string",
|
||||||
|
Required: true,
|
||||||
|
Unique: true,
|
||||||
|
Default: nil,
|
||||||
|
Validations: []sdk.Validation{
|
||||||
|
{
|
||||||
|
Type: "validate",
|
||||||
|
Value: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Relationship: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "password",
|
||||||
|
Type: "string",
|
||||||
|
Required: true,
|
||||||
|
Unique: false,
|
||||||
|
Default: nil,
|
||||||
|
Validations: []sdk.Validation{
|
||||||
|
{
|
||||||
|
Type: "validate",
|
||||||
|
Value: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Relationship: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "created_at",
|
||||||
|
Type: "datetime",
|
||||||
|
Required: true,
|
||||||
|
Unique: false,
|
||||||
|
Default: nil,
|
||||||
|
Validations: []sdk.Validation{
|
||||||
|
{
|
||||||
|
Type: "validate",
|
||||||
|
Value: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Relationship: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "updated_at",
|
||||||
|
Type: "datetime",
|
||||||
|
Required: true,
|
||||||
|
Unique: false,
|
||||||
|
Default: nil,
|
||||||
|
Validations: []sdk.Validation{
|
||||||
|
{
|
||||||
|
Type: "validate",
|
||||||
|
Value: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Relationship: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
createUserDesc := "Create a new user with username, email, and password. Returns the created user object."
|
||||||
|
createUser := sdk.Endpoint{
|
||||||
|
Method: "POST",
|
||||||
|
Path: "/user",
|
||||||
|
Entity: userEntity,
|
||||||
|
Description: &createUserDesc,
|
||||||
|
Auth: true,
|
||||||
|
Permissions: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
server := sdk.Server{
|
||||||
|
Name: "TestServer",
|
||||||
|
Settings: []sdk.ServerSetting{
|
||||||
|
{
|
||||||
|
Host: "localhost",
|
||||||
|
Port: 8000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Endpoints: []sdk.Endpoint{
|
||||||
|
createUser,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def := sdk.Definition{
|
||||||
|
Server: []sdk.Server{
|
||||||
|
server,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := json.MarshalIndent(def, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
println(string(out))
|
||||||
|
println("🎉 Successfully built SDK definition!")
|
||||||
|
println("You can now use this SDK definition in your application.")
|
||||||
|
}
|
1
go.mod
1
go.mod
@ -8,6 +8,7 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/alecthomas/participle/v2 v2.1.4 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
|
||||||
|
2
go.sum
2
go.sum
@ -1,3 +1,5 @@
|
|||||||
|
github.com/alecthomas/participle/v2 v2.1.4 h1:W/H79S8Sat/krZ3el6sQMvMaahJ+XcM9WSI2naI7w2U=
|
||||||
|
github.com/alecthomas/participle/v2 v2.1.4/go.mod h1:8tqVbpTX20Ru4NfYQgZf4mP18eXPTBViyMWiArNEgGI=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
|
181
lang/lang.go
Normal file
181
lang/lang.go
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
package lang
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/alecthomas/participle/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Root AST node containing all definitions
|
||||||
|
type AST struct {
|
||||||
|
Definitions []Definition `parser:"@@*"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Union type for top-level definitions
|
||||||
|
type Definition struct {
|
||||||
|
Server *Server `parser:"@@"`
|
||||||
|
Entity *Entity `parser:"| @@"`
|
||||||
|
Endpoint *Endpoint `parser:"| @@"`
|
||||||
|
Page *Page `parser:"| @@"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean server syntax
|
||||||
|
type Server struct {
|
||||||
|
Name string `parser:"'server' @Ident"`
|
||||||
|
Settings []ServerSetting `parser:"@@*"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerSetting struct {
|
||||||
|
Host *string `parser:"('host' @String)"`
|
||||||
|
Port *int `parser:"| ('port' @Int)"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean entity syntax with better readability
|
||||||
|
type Entity struct {
|
||||||
|
Name string `parser:"'entity' @Ident"`
|
||||||
|
Description *string `parser:"('desc' @String)?"`
|
||||||
|
Fields []Field `parser:"@@*"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Much cleaner field syntax
|
||||||
|
type Field struct {
|
||||||
|
Name string `parser:"@Ident ':'"`
|
||||||
|
Type string `parser:"@Ident"`
|
||||||
|
Required bool `parser:"@'required'?"`
|
||||||
|
Unique bool `parser:"@'unique'?"`
|
||||||
|
Index bool `parser:"@'indexed'?"`
|
||||||
|
Default *string `parser:"('default' @String)?"`
|
||||||
|
Validations []Validation `parser:"@@*"`
|
||||||
|
Relationship *Relationship `parser:"@@?"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple validation syntax
|
||||||
|
type Validation struct {
|
||||||
|
Type string `parser:"'validate' @Ident"`
|
||||||
|
Value *string `parser:"@String?"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear relationship syntax
|
||||||
|
type Relationship struct {
|
||||||
|
Type string `parser:"'relates' 'to' @Ident"`
|
||||||
|
Cardinality string `parser:"'as' @('one' | 'many')"`
|
||||||
|
ForeignKey *string `parser:"('via' @String)?"`
|
||||||
|
Through *string `parser:"('through' @String)?"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Endpoint definitions with clean, readable syntax
|
||||||
|
type Endpoint struct {
|
||||||
|
Method string `parser:"'endpoint' @('GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH')"`
|
||||||
|
Path string `parser:"@String"`
|
||||||
|
Entity *string `parser:"('for' @Ident)?"`
|
||||||
|
Description *string `parser:"('desc' @String)?"`
|
||||||
|
Auth bool `parser:"@'auth'?"`
|
||||||
|
Params []EndpointParam `parser:"@@*"`
|
||||||
|
Response *ResponseSpec `parser:"@@?"`
|
||||||
|
CustomLogic *string `parser:"('custom' @String)?"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean parameter syntax
|
||||||
|
type EndpointParam struct {
|
||||||
|
Name string `parser:"'param' @Ident ':'"`
|
||||||
|
Type string `parser:"@Ident"`
|
||||||
|
Required bool `parser:"@'required'?"`
|
||||||
|
Source string `parser:"'from' @('path' | 'query' | 'body')"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response specification
|
||||||
|
type ResponseSpec struct {
|
||||||
|
Type string `parser:"'returns' @Ident"`
|
||||||
|
Format *string `parser:"('as' @String)?"`
|
||||||
|
Fields []string `parser:"('fields' '[' @Ident (',' @Ident)* ']')?"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Page definitions for frontend with clean syntax
|
||||||
|
type Page struct {
|
||||||
|
Name string `parser:"'page' @Ident"`
|
||||||
|
Path string `parser:"'at' @String"`
|
||||||
|
Layout string `parser:"'layout' @Ident"`
|
||||||
|
Title *string `parser:"('title' @String)?"`
|
||||||
|
Description *string `parser:"('desc' @String)?"`
|
||||||
|
Auth bool `parser:"@'auth'?"`
|
||||||
|
Meta []MetaTag `parser:"@@*"`
|
||||||
|
Components []Component `parser:"@@*"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Meta tags for SEO
|
||||||
|
type MetaTag struct {
|
||||||
|
Name string `parser:"'meta' @Ident"`
|
||||||
|
Content string `parser:"@String"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Component definitions with endpoint references
|
||||||
|
type Component struct {
|
||||||
|
Type string `parser:"'component' @Ident"`
|
||||||
|
Entity *string `parser:"('for' @Ident)?"`
|
||||||
|
Config []ComponentAttr `parser:"@@*"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Component attributes and configurations
|
||||||
|
type ComponentAttr struct {
|
||||||
|
Fields *ComponentFields `parser:"@@"`
|
||||||
|
Actions *ComponentActions `parser:"| @@"`
|
||||||
|
DataSource *ComponentDataSource `parser:"| @@"`
|
||||||
|
Style *ComponentStyle `parser:"| @@"`
|
||||||
|
Pagination *ComponentPagination `parser:"| @@"`
|
||||||
|
Filters *ComponentFilters `parser:"| @@"`
|
||||||
|
Validation bool `parser:"| @'validate'"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Component field specification
|
||||||
|
type ComponentFields struct {
|
||||||
|
Fields []string `parser:"'fields' '[' @Ident (',' @Ident)* ']'"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Component actions (can reference endpoints)
|
||||||
|
type ComponentActions struct {
|
||||||
|
Actions []ComponentAction `parser:"'actions' '[' @@ (',' @@)* ']'"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ComponentAction struct {
|
||||||
|
Name string `parser:"@Ident"`
|
||||||
|
Endpoint *string `parser:"('via' @String)?"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data source configuration (can reference endpoints)
|
||||||
|
type ComponentDataSource struct {
|
||||||
|
Endpoint string `parser:"'data' 'from' @String"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Component styling
|
||||||
|
type ComponentStyle struct {
|
||||||
|
Theme *string `parser:"'style' @Ident"`
|
||||||
|
Classes []string `parser:"('classes' '[' @String (',' @String)* ']')?"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pagination configuration
|
||||||
|
type ComponentPagination struct {
|
||||||
|
PageSize *int `parser:"'pagination' ('size' @Int)?"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter specifications
|
||||||
|
type ComponentFilters struct {
|
||||||
|
Filters []ComponentFilter `parser:"'filters' '[' @@ (',' @@)* ']'"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ComponentFilter struct {
|
||||||
|
Field string `parser:"@Ident"`
|
||||||
|
Type string `parser:"'as' @('text' | 'select' | 'date' | 'number')"`
|
||||||
|
Label *string `parser:"('label' @String)?"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(input string) (AST, error) {
|
||||||
|
parser, err := participle.Build[AST](
|
||||||
|
participle.Unquote("String"),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return AST{}, err
|
||||||
|
}
|
||||||
|
ast, err := parser.ParseString("", input)
|
||||||
|
if err != nil {
|
||||||
|
return AST{}, err
|
||||||
|
}
|
||||||
|
return *ast, nil
|
||||||
|
}
|
1114
lang/lang_test.go
Normal file
1114
lang/lang_test.go
Normal file
File diff suppressed because it is too large
Load Diff
79
langV2/lang.go
Normal file
79
langV2/lang.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package langV2
|
||||||
|
|
||||||
|
import (
|
||||||
|
participle "github.com/alecthomas/participle/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Root AST node containing all definitions
|
||||||
|
type AST struct {
|
||||||
|
Definitions []Definition `parser:"@@*"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Definition struct {
|
||||||
|
Entity *EntityDef `parser:"@@"`
|
||||||
|
Server *ServerDef `parser:"| @@"`
|
||||||
|
Page *PageDef `parser:"| @@"`
|
||||||
|
Component *ComponentDef `parser:"| @@"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type EntityDef struct {
|
||||||
|
Name string `parser:"'entity' @Ident"`
|
||||||
|
Fields []Field `parser:"'{' @@* '}'"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Field struct {
|
||||||
|
Name string `parser:"@Ident ':'"`
|
||||||
|
Type string `parser:"@Ident"`
|
||||||
|
Required bool `parser:"@'required'?"`
|
||||||
|
Unique bool `parser:"@'unique'?"`
|
||||||
|
Index bool `parser:"@'indexed'?"`
|
||||||
|
Default *string `parser:"('default' @String)?"`
|
||||||
|
Validations []Validation `parser:"@@*"`
|
||||||
|
Relationship *Relationship `parser:"@@?"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Validation struct {
|
||||||
|
Type string `parser:"'validate' @Ident"`
|
||||||
|
Value *string `parser:"@String?"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Relationship struct {
|
||||||
|
Type string `parser:"'relates' 'to' @Ident"`
|
||||||
|
Cardinality string `parser:"'as' @('one' | 'many')"`
|
||||||
|
ForeignKey *string `parser:"('via' @String)?"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerDef struct {
|
||||||
|
Name string `parser:"'server' @Ident"`
|
||||||
|
Endpoints []EndpointDef `parser:"'{' @@* '}'"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type EndpointDef struct {
|
||||||
|
Method string `parser:"@('GET'|'POST'|'PUT'|'DELETE')"`
|
||||||
|
Path string `parser:"@String"`
|
||||||
|
EntityRef string `parser:"'entity' @Ident"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ComponentDef struct {
|
||||||
|
EntityRef string `parser:"@Ident"`
|
||||||
|
ServerRef string `parser:"('from' @Ident)?"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PageDef struct {
|
||||||
|
Name string `parser:"'page' @Ident"`
|
||||||
|
Components []ComponentDef `parser:"'{' @@* '}'"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(input string) (AST, error) {
|
||||||
|
parser, err := participle.Build[AST](
|
||||||
|
participle.Unquote("String"),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return AST{}, err
|
||||||
|
}
|
||||||
|
ast, err := parser.ParseString("", input)
|
||||||
|
if err != nil {
|
||||||
|
return AST{}, err
|
||||||
|
}
|
||||||
|
return *ast, nil
|
||||||
|
}
|
91
sdk/sdk.go
Normal file
91
sdk/sdk.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package sdk
|
||||||
|
|
||||||
|
type Definition struct {
|
||||||
|
Server []Server `json:"server,omitempty"`
|
||||||
|
Entity []Entity `json:"entity,omitempty"`
|
||||||
|
Endpoint []Endpoint `json:"endpoint,omitempty"`
|
||||||
|
Page []Page `json:"page,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Settings []ServerSetting `json:"settings,omitempty"`
|
||||||
|
Endpoints []Endpoint `json:"endpoints,omitempty"` // Optional endpoints for the server
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerSetting struct {
|
||||||
|
Host string `json:"host,omitempty"`
|
||||||
|
Port int `json:"port,omitempty"`
|
||||||
|
// TODO: Add more settings as needed e.g. TLS, CORS, etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
type Entity struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
Fields []Field `json:"fields,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Field struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Required bool `json:"required,omitempty"`
|
||||||
|
Unique bool `json:"unique,omitempty"`
|
||||||
|
Index bool `json:"index,omitempty"`
|
||||||
|
Default *string `json:"default,omitempty"`
|
||||||
|
Validations []Validation `json:"validations,omitempty"`
|
||||||
|
Relationship *Relationship `json:"relationship,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Validation struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Value *string `json:"value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Relationship struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Cardinality string `json:"cardinality"`
|
||||||
|
ForeignKey *string `json:"foreign_key,omitempty"`
|
||||||
|
Through *string `json:"through,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Endpoint struct {
|
||||||
|
Method string `json:"method"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Entity Entity `json:"entity,omitempty"`
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
Auth bool `json:"auth,omitempty"`
|
||||||
|
Permissions []string `json:"permissions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Page struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Title *string `json:"title,omitempty"`
|
||||||
|
Description *string `json:"description,omitempty"`
|
||||||
|
Auth bool `json:"auth,omitempty"`
|
||||||
|
Permissions []string `json:"permissions,omitempty"`
|
||||||
|
Components []Component `json:"components,omitempty"` // List of components used in the page
|
||||||
|
Styles []string `json:"styles,omitempty"` // List of stylesheets or CSS
|
||||||
|
Script []string `json:"script,omitempty"` // List of scripts or JS files
|
||||||
|
Metadata map[string]string `json:"metadata,omitempty"` // Additional metadata for the page
|
||||||
|
}
|
||||||
|
|
||||||
|
type Component struct {
|
||||||
|
Name string `json:"name"` // Name of the component
|
||||||
|
Description *string `json:"description,omitempty"` // Optional description of the component
|
||||||
|
Props []Prop `json:"props,omitempty"` // List of properties for the component
|
||||||
|
Type string `json:"type"` // Type of the component (e.g., "form", "table", etc.)
|
||||||
|
Entity Entity `json:"entity,omitempty"` // Optional entity this component is associated with
|
||||||
|
Styles []string `json:"styles,omitempty"` // List of stylesheets or CSS for the component
|
||||||
|
Script []string `json:"script,omitempty"` // List of scripts or JS files for the component
|
||||||
|
}
|
||||||
|
|
||||||
|
type Prop struct {
|
||||||
|
Name string `json:"name"` // Name of the property
|
||||||
|
Description *string `json:"description,omitempty"` // Optional description of the property
|
||||||
|
Type string `json:"type"` // Type of the property (e.g., "string", "number", "boolean", etc.)
|
||||||
|
Required bool `json:"required,omitempty"` // Whether the property is required
|
||||||
|
Default *string `json:"default,omitempty"` // Default value for the property
|
||||||
|
Validations []Validation `json:"validations,omitempty"` // List of validations for the property
|
||||||
|
Relationship *Relationship `json:"relationship,omitempty"` // Optional relationship
|
||||||
|
}
|
Reference in New Issue
Block a user