Commit f8c48d4b authored by mohamad.alturky's avatar mohamad.alturky

Refactor

parent 10228078
package FileManagement
import (
"bufio"
"log"
"os"
)
/**
* reads file data and returns it as a string
*/
func ReadData(fileName string) string {
file, err := os.Open(fileName)
if err != nil {
log.Fatal(err)
}
defer file.Close()
data := ""
scanner := bufio.NewScanner(file)
for scanner.Scan() {
data = data + scanner.Text() + "\n"
}
return data
}
/**
* writes text data to a file
*/
func WriteFile(filePath string, text string) {
f, err := os.Create(filePath)
if err != nil {
log.Fatal(err)
}
defer f.Close()
_, err = f.WriteString(text)
if err != nil {
log.Fatal(err)
}
}
module donut
go 1.22.0
require github.com/TwiN/go-color v1.4.1
github.com/TwiN/go-color v1.4.1 h1:mqG0P/KBgHKVqmtL5ye7K0/Gr4l6hTksPgTgMk3mUzc=
github.com/TwiN/go-color v1.4.1/go.mod h1:WcPf/jtiW95WBIsEeY1Lc/b8aaWoiqQpu5cf8WFxu+s=
package main
import (
FileManagement "donut/fileManagement"
Transpilation "donut/transpilation"
)
func main() {
template := `
@template
{{
text
@exec
{{
code
@template
{{
text
}}
code
}}
text
@exec
{{ 11111111
code
@template
{{
text
}}
code
}}
}}
@define {{
f()===>
}}
@import {{
"fmt"
}}
`
result := Transpilation.BuildTranspiler(template).GetExecutable()
FileManagement.WriteFile("./rs.go", result)
}
// package main
// import (
// Tokenizer "donut/tokenization"
// "github.com/TwiN/go-color"
// )
// func main() {
// template := `
// @template
// {{
// text
// @exec
// {{
// code
// @template
// {{
// text
// }}
// code
// }}
// text
// @exec
// {{ 11111111
// code
// @template
// {{
// text
// }}
// code
// }}
// }}
// `
// new := ""
// startingIdx := 0
// for {
// if startingIdx == len(template) {
// break
// }
// res := Tokenizer.ConvertNestingToConcatenation(startingIdx, template, "exec", "template")
// startingIdx = res.TerminatedAt
// new = new + res.Template
// }
// // println("------------------")
// // println(template2.Template)
// // println("------------------")
// // println(template2.TerminatedAt)
// // println("------------------")
// // println(len(template))
// println(color.Colorize(color.Blue, `
// |‾‾‾‾‾‾‾‾\ |‾‾‾‾‾‾‾‾‾| |‾‾‾‾‾\ |‾‾| |‾‾| |‾‾| |‾‾‾‾‾‾‾‾‾‾‾‾|
// | |‾‾‾\ \ | |‾‾‾| | | |\ \ | | | | | | ‾‾‾‾| |‾‾‾‾
// | | | | | | | | | | \ \| | | | | | | |
// | |___/ / | |___| | | | \ | | |___| | | |
// |________/ |_________| |__| \____| |_________| |__|
// `))
// println(new)
// // println(color.Colorize(color.Red, "This is also red"))
// }
// // package main
// // // A simple program demonstrating the paginator component from the Bubbles
// // // component library.
// // import (
// // "fmt"
// // "log"
// // "strings"
// // "github.com/charmbracelet/bubbles/paginator"
// // "github.com/charmbracelet/lipgloss"
// // tea "github.com/charmbracelet/bubbletea"
// // )
// // func newModel() model {
// // var items []string
// // for i := 1; i < 101; i++ {
// // text := fmt.Sprintf("Item %d", i)
// // items = append(items, text)
// // }
// // p := paginator.New()
// // p.Type = paginator.Dots
// // p.PerPage = 10
// // p.ActiveDot = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "235", Dark: "252"}).Render("•")
// // p.InactiveDot = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "250", Dark: "238"}).Render("•")
// // p.SetTotalPages(len(items))
// // return model{
// // paginator: p,
// // items: items,
// // }
// // }
// // type model struct {
// // items []string
// // paginator paginator.Model
// // }
// // func (m model) Init() tea.Cmd {
// // return nil
// // }
// // func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
// // var cmd tea.Cmd
// // switch msg := msg.(type) {
// // case tea.KeyMsg:
// // switch msg.String() {
// // case "q", "esc", "ctrl+c":
// // return m, tea.Quit
// // }
// // }
// // m.paginator, cmd = m.paginator.Update(msg)
// // return m, cmd
// // }
// // func (m model) View() string {
// // var b strings.Builder
// // b.WriteString("\n Paginator Example\n\n")
// // start, end := m.paginator.GetSliceBounds(len(m.items))
// // for _, item := range m.items[start:end] {
// // b.WriteString(" • " + item + "\n\n")
// // }
// // b.WriteString(" " + m.paginator.View())
// // b.WriteString("\n\n h/l ←/→ page • q: quit\n")
// // return b.String()
// // }
// // func main() {
// // p := tea.NewProgram(newModel())
// // if _, err := p.Run(); err != nil {
// // log.Fatal(err)
// // }
// // }
package main
import(
"fmt"
)
func main() {
Code := ""
Code = Code + string( `
text
`)
code
Code = Code + string( `
text
`)
code
Code = Code + string( `
text
`)
11111111
code
Code = Code + string( `
text
`)
code
Code = Code + string( `
`)
}
f()===>
package Settings
const IMPORT_KEY_WORD = "@import"
const DEFINE_KEY_WORD = "@define"
const EXECUTE_KEY_WORD = "@exec"
const TEMPLATE_KEY_WORD = "@template"
const FINAL_GENERATED_CODE_CONTAINER = "CODE_CONTAINER"
package Tokenizer
import (
Types "donut/types"
"regexp"
)
/*
*
- gets a template then extract the content of the directive
- and mutaute it to remove the content of it.
*/
func ExtractDirectiveContent(template string, directiveName string) [2]string {
/**
* regex to find the directive keyword, it will return list of matches
* the match is stuctured like this [starting_index,end_index]
*/
re := regexp.MustCompile(directiveName)
/**
* the places where the directive keyword found
*/
directiveMatches := re.FindAllStringIndex(template, -1)
/**
* the index that we will use to loop over the template
*/
loopIndex := 0
/**
* the index of where the start `{{` found
*/
indexOfOpenedBracket := 0
/**
* the index of where the start `}}` found
*/
directiveFinishedFrom := 0
/**
* loop over the template from the index where we found
* the directive keyword until we find the `}}`
*/
for loopIndex = directiveMatches[0][0]; loopIndex < len(template); loopIndex++ {
/**
* find `{{` and same its index
*/
if template[loopIndex] == '{' && template[loopIndex+1] == '{' {
indexOfOpenedBracket = loopIndex
}
/**
* break the loop because we found the temination token
*/
if template[loopIndex] == '}' && template[loopIndex+1] == '}' {
break
}
}
/**
* the directive content that will be returned from the function
*/
directiveContent := template[indexOfOpenedBracket+2 : loopIndex]
/**
* the index where we finished the parsing of the directive content
*/
directiveFinishedFrom = loopIndex + 2
/**
* mutate the template to return the old template but cleaned from the directive content
*/
newTemplate := template[:directiveMatches[0][0]] + template[directiveFinishedFrom:]
return [2]string{directiveContent, newTemplate}
}
/*
*
- gets a template then extract the content of the directive
- and returns the index where it ended
*/
func GetDirectiveContentWithTerminationIndex(template string, directiveName string) Types.DirectiveContentSpecification {
/**
* regex to find the directive keyword, it will return list of matches
* the match is stuctured like this [starting_index,end_index]
*/
re := regexp.MustCompile(directiveName)
/**
* the places where the directive keyword found
*/
directiveMatches := re.FindAllStringIndex(template, -1)
/**
* the index that we will use to loop over the template
*/
loopIndex := 0
/**
* the index of where the start `{{` found
*/
indexOfOpenedBracket := 0
/**
* loop over the template from the index where we found
* the directive keyword until we find the `}}`
*/
for loopIndex = directiveMatches[0][0]; loopIndex < len(template); loopIndex++ {
/**
* find `{{` and same its index
*/
if template[loopIndex] == '{' && template[loopIndex+1] == '{' {
indexOfOpenedBracket = loopIndex
}
/**
* break the loop because we found the temination token
*/
if template[loopIndex] == '}' && template[loopIndex+1] == '}' {
break
}
}
/**
* the directive content that will be returned from the function
*/
directiveContent := template[indexOfOpenedBracket+2 : loopIndex]
/**
* the index where we finished the parsing of the directive content
*/
directiveFinishedFrom := loopIndex + 2
return Types.DirectiveContentSpecification{Content: directiveContent, TerminationIndex: directiveFinishedFrom}
}
func FindTerminationIndex(template string, startingIndex int) int {
counter := 0
passed := false
k := startingIndex
indexOfClosing := k
for ; k < len(template); k++ {
if counter == 0 && passed {
indexOfClosing = k + 1
break
}
if template[k] == '}' && template[k+1] == '}' {
passed = true
counter = counter - 1
}
if template[k] == '{' && template[k+1] == '{' {
passed = true
counter = counter + 1
}
}
return indexOfClosing
}
package Tokenizer
import (
Types "donut/types"
"regexp"
)
/*
*
- gets a template then replace all occurences of it with the fn(its content)
fn is the transformation that we want to apply to the content of the template
- returns the updated template
*/
func ReplaceDirectiveContent(template string, directiveName string, fn Types.TextToTextConverter) string {
/**
* regex to find the directive keyword, it will return list of matches
* the match is stuctured like this [starting_index,end_index]
*/
re := regexp.MustCompile(directiveName)
/**
* the places where the directive keyword found
*/
directiveMatches := re.FindAllStringIndex(template, -1)
/**
* pair of values
* the first is the content of the directive
* the second is the index where the directive is terminated
*/
var directivesContentSpecification = []Types.DirectiveContentSpecification{}
/**
* loop over the occurences of the directive
* gets the directive content specification
*/
for _, tuple := range directiveMatches {
dc := GetDirectiveContentWithTerminationIndex(template[tuple[0]:], directiveName)
directivesContentSpecification = append(directivesContentSpecification, dc)
}
/**
* the new template will contain the old one but
* we will replace all occurences of the directive with its content
* with the function fn
*/
newTemplate := ""
/**
* index to add to the new template the old value from this index
* to the start of the directive, so that we can accumulate the new template
*/
leftIndex := 0
/**
* loop over the occurences of the directive
* and construct the new template
*/
for index, tuple := range directiveMatches {
// adding the content before the directive
newTemplate = newTemplate + template[leftIndex:tuple[0]]
// adding the transformed content of the directive
newTemplate = newTemplate + fn(directivesContentSpecification[index].Content)
// update the leftIndex it will be equal to the index where the directive closed
leftIndex = directivesContentSpecification[index].TerminationIndex + tuple[0]
}
// adding the remaining template which will be the same of the old because there isn't any directive occurence in it
newTemplate = newTemplate + template[leftIndex:]
return newTemplate
}
package Tokenizer
import (
Types "donut/types"
)
func ConvertNestingToConcatenation(startAt int, template string, executeToken string, templateToken string) Types.RecursionResult {
/*
* the answer is the string where we will save the mutated template
*/
answer := ""
/*
* the termination index will contain the index where the prosses terminated
*/
terminationIndex := len(template)
/*
* loop over the template from the parameter @startingIndex
* the end of the scope which is where the nesting scope end
*/
for index := startAt; index < len(template); {
/*
* adding the characher to the template
*/
answer = answer + string(template[index])
/**
* increment the index because we handle this charachter
*/
index++
/**
* start handling the nesting
*/
if template[index-1] == '@' {
/**
* case of templateToken scope
*/
if template[index:index+len(templateToken)] == templateToken {
/**
* adding the templateToken after the @ so that the template will not change
*/
answer = answer + templateToken
/**
* jump to the index after the templateToken
*/
index = index + len(templateToken)
/**
* the index where the templateToken scope end
*/
terminationIndex = FindTerminationIndex(template, index)
/**
* start proccessing the inner scope
*/
for idx := index; idx < terminationIndex; {
/**
* if we found a nested scope we will recursively handle it
*/
if template[idx] == '@' {
/**
* get the result of the recursive function
*/
next := ConvertNestingToConcatenation(idx, template, executeToken, templateToken)
/**
* jump to the terminated index of the inner nested block
*/
idx = next.TerminatedAt
/**
* update the answer to concate the result of the inner nested block
*/
answer = answer + "}" + "}" + next.Template + "@" + templateToken + "{" + "{"
} else {
/**
* add the character directly because the is no nesting
*/
answer = answer + string(template[idx])
/**
* increament the loop index
*/
idx++
}
}
/**
* terminate the process
*/
index = terminationIndex
break
}
/**
* case of executeToken scope
*/
if template[index:index+len(executeToken)] == executeToken {
/**
* adding the executeToken after the @ so that the template will not change
*/
answer = answer + executeToken
/**
* jump to the index after the executeToken
*/
index = index + len(executeToken)
/**
* the index where the executeToken scope end
*/
terminationIndex = FindTerminationIndex(template, index)
/**
* start proccessing the inner scope
*/
for idx := index; idx < terminationIndex; {
/**
* if we found a nested scope we will recursively handle it
*/
if template[idx] == '@' {
/**
* get the result of the recursive function
*/
next := ConvertNestingToConcatenation(idx, template, executeToken, templateToken)
/**
* jump to the terminated index of the inner nested block
*/
idx = next.TerminatedAt
/**
* update the answer to concate the result of the inner nested block
*/
answer = answer + "}" + "}" + next.Template + "@" + executeToken + "{" + "{"
} else {
/**
* add the character directly because the is no nesting
*/
answer = answer + string(template[idx])
/**
* increament the loop index
*/
idx++
}
}
/**
* terminate the process
*/
index = terminationIndex
break
}
}
}
return Types.RecursionResult{Template: answer, TerminatedAt: terminationIndex}
}
package Transpilation
type ITranspiler interface {
GetExecutable() func()
}
package Transpilation
import Tokenizer "donut/tokenization"
type Transpiler struct {
template string
}
func BuildTranspiler(template string) Transpiler {
return Transpiler{template: template}
}
func (transpiler Transpiler) GetExecutable() string {
importsParsingResult := Tokenizer.ExtractDirectiveContent(transpiler.template, "@import")
transpiler.template = importsParsingResult[1]
println("transpiler.template")
println(transpiler.template)
println("transpiler.template")
importsContent := importsParsingResult[0]
println(importsContent)
defineParsingResult := Tokenizer.ExtractDirectiveContent(transpiler.template, "@define")
transpiler.template = defineParsingResult[1]
defineContent := defineParsingResult[0]
println(defineContent)
templateAfterAdjustingInjectables := Tokenizer.ReplaceDirectiveContent(transpiler.template, "@inject", returnInjectedString)
transpiler.template = templateAfterAdjustingInjectables
println("templateAfterAdjustingInjectables")
println(templateAfterAdjustingInjectables)
println("templateAfterAdjustingInjectables")
resultAfterReplacingNesting := ""
startingIdx := 0
for {
println(startingIdx)
if startingIdx == len(transpiler.template) {
break
}
res := Tokenizer.ConvertNestingToConcatenation(startingIdx, transpiler.template, "exec", "template")
startingIdx = res.TerminatedAt
resultAfterReplacingNesting = resultAfterReplacingNesting + res.Template
}
transpiler.template = resultAfterReplacingNesting
templateSectionsParsed := Tokenizer.ReplaceDirectiveContent(transpiler.template, "@template", returnInjectedtemplate)
transpiler.template = templateSectionsParsed
execSectionsParsed := Tokenizer.ReplaceDirectiveContent(transpiler.template, "@exec", returnCode)
transpiler.template = execSectionsParsed
finalCode := injectImports(importsContent) + injectMain(transpiler.template) + defineContent
return finalCode
}
func returnInjectedString(text string) string {
return "\nCode = Code + string( " + text + " )\n"
}
func returnInjectedtemplate(text string) string {
return "\nCode = Code + string( `" + text + "`)\n"
}
func returnCode(text string) string {
return text
}
func injectImports(imports string) string {
return `
package main
import(
` + imports + `
)
`
}
func injectMain(main string) string {
return `
func main() {
Code := ""
` + main + `
}
`
}
package Types
/*
*
- contains the content of the directive
ex. the directive will be like this
@directive {{ content }}
- the termination index that the }} appeared
ex. if the the index = 6 of the first } in this termination token "}}"
the TerminationIndex = 8 the next index that is'nt }
*/
type DirectiveContentSpecification struct {
Content string
TerminationIndex int
}
package Types
/*
* RecursionResult is the container for the output of the function ConvertNestingToConcatenation
- it will return the converted template
- and the index where the function finished its proccess
*/
type RecursionResult struct {
Template string
TerminatedAt int
}
package Types
/*
*
- type of functions gets two texts and convert them and make them in a desirable order
*/
type TextAppendUtil func(string, string) string
package Types
/*
*
- type of functions gets string and return another one
*/
type TextToTextConverter func(string) string
@import {{
"fmt"
}}
using Go;
@template {{
}}
public class Main
{
@exec {{
for i:= 0; i < 10; i++ {
@inject {{ "Go" }}
}
}}
}
@define {{
}}
\ No newline at end of file
package tokenization_test
import (
// Settings "donut/settings"
Tokenizer "donut/tokenization"
"testing"
)
func TestConvertNestingToConcatenation_ShouldReturnTheExpectedResult_WhenTheTemplateContainsNewLinesBetweenDirectives(t *testing.T) {
template :=
`
@template {{ some code.... @exec {{ nothing }} }}
@exec {{ some go code.... }}
@imports {{ "tesing_package" }}
@template {{ some code.... }}
@exec {{ some go code.... }}
`
expectedTemplate :=
`
@template {{ some code.... }}@exec {{ nothing }}@template{{ }}
@exec {{ some go code.... }}
@imports {{ "tesing_package" }}
@template {{ some code.... }}
@exec {{ some go code.... }}
`
new := ""
startingIdx := 0
for {
if startingIdx == len(template) {
break
}
res := Tokenizer.ConvertNestingToConcatenation(startingIdx, template, "exec", "template")
startingIdx = res.TerminatedAt
new = new + res.Template
}
// t.Log(new)
if new != expectedTemplate {
t.Error("error in getting template")
}
}
func TestConvertNestingToConcatenation_ShouldReturnTheExpectedResult_WhenTheTemplateStacksDirectivesWithNoSpaces(t *testing.T) {
template :=
`
@template {{ some code.... @exec{{nothing}}555@exec{{nothing}} }}@exec {{ some go code.... }}
@template {{ "tesing_package"@exec{{nothing}} }}
@template {{ some code.... }}
@exec {{ some go code.... }}
`
expectedTemplate :=
`
@template {{ some code.... }}@exec{{nothing}}@template{{555}}@exec{{nothing}}@template{{ }}@exec {{ some go code.... }}
@template {{ "tesing_package"}}@exec{{nothing}}@template{{ }}
@template {{ some code.... }}
@exec {{ some go code.... }}
`
new := ""
startingIdx := 0
for {
if startingIdx == len(template) {
break
}
res := Tokenizer.ConvertNestingToConcatenation(startingIdx, template, "exec", "template")
startingIdx = res.TerminatedAt
new = new + res.Template
}
t.Log(new)
if new != expectedTemplate {
t.Error("error in getting template")
}
}
package tokenization_test
import (
Settings "donut/settings"
Tokenizer "donut/tokenization"
"testing"
)
func TestExtractDirectiveContent_ShouldReturnTheExpectedResult_WhenTheTemplateContainsNewLinesBetweenDirectives(t *testing.T) {
template :=
`
@template {{ some code.... }}
@exec {{ some go code.... }}
@imports {{ "tesing_package" }}
@template {{ some code.... }}
@exec {{ some go code.... }}
`
expectedImports := ` "tesing_package" `
expectedTemplate :=
`
@template {{ some code.... }}
@exec {{ some go code.... }}
@template {{ some code.... }}
@exec {{ some go code.... }}
`
result := Tokenizer.ExtractDirectiveContent(template, Settings.IMPORT_KEY_WORD)
if result[0] != expectedImports {
t.Error("error in getting imports")
}
if result[1] != expectedTemplate {
t.Error("error in getting template")
}
}
func TestExtractDirectiveContent_ShouldReturnTheExpectedResult_WhenTheTemplateContainsSpacesBetweenDirectives(t *testing.T) {
template :=
`
@template {{ some code.... }} @exec {{ some go code.... }} @imports {{"tesing_package" }} @template {{ some code.... }} @exec {{ some go code.... }}
`
expectedImports := `"tesing_package" `
expectedTemplate :=
`
@template {{ some code.... }} @exec {{ some go code.... }} @template {{ some code.... }} @exec {{ some go code.... }}
`
result := Tokenizer.ExtractDirectiveContent(template, Settings.IMPORT_KEY_WORD)
if result[0] != expectedImports {
t.Error("error in getting imports")
}
if result[1] != expectedTemplate {
t.Error("error in getting template")
}
}
func TestExtractDirectiveContent_ShouldReturnTheExpectedResult_WhenTheTemplateStacksDirectivesWithNoSpaces(t *testing.T) {
template :=
`
@template {{ some code.... }}@exec {{ some go code.... }}54646@imports{{ 8 "tesing_package"}}@template{{ some code.... }}@exec{{ some go code.... }}
`
expectedImports := ` 8 "tesing_package"`
expectedTemplate :=
`
@template {{ some code.... }}@exec {{ some go code.... }}54646@template{{ some code.... }}@exec{{ some go code.... }}
`
result := Tokenizer.ExtractDirectiveContent(template, Settings.IMPORT_KEY_WORD)
if result[0] != expectedImports {
t.Error("error in getting imports")
}
if result[1] != expectedTemplate {
t.Error("error in getting template")
t.Log(result[1])
}
}
package tokenization_test
import (
Tokenizer "donut/tokenization"
"testing"
)
func TestReplaceDirectiveContentWithTemplateWithNewLinesBetweenDirectives(t *testing.T) {
template :=
`----
@template {{ some code.... }}
@exec {{
for i := 0; i < 7; i++ {
@inject {{ string(i) }}
}
}}
@template {{ some code.... }}
@exec {{ some go code.... }}
------
`
expectedTemplate :=
`----
@template {{ some code.... }}
@exec {{
for i := 0; i < 7; i++ {
code = code + string(i)
}
}}
@template {{ some code.... }}
@exec {{ some go code.... }}
------
`
result := Tokenizer.ReplaceDirectiveContent(template, "@inject", Identity)
t.Log(result)
if result != expectedTemplate {
t.Error("error in getting template")
}
}
func TestReplaceDirectiveContentWithTemplateWithSpacesBetweenDirectives(t *testing.T) {
template :=
`----
@template {{ some code.... }}
@exec {{
for i := 0; i < 7; i++ { @inject {{ string(i) }} }
}}
@template {{ some code.... }}
@exec {{ some go code.... }}
------
`
expectedTemplate :=
`----
@template {{ some code.... }}
@exec {{
for i := 0; i < 7; i++ { code = code + string(i) }
}}
@template {{ some code.... }}
@exec {{ some go code.... }}
------
`
result := Tokenizer.ReplaceDirectiveContent(template, "@inject", Identity)
t.Log(result)
if result != expectedTemplate {
t.Error("error in getting template")
}
}
func Identity(text string) string {
return "code = " + " code + " + text
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment