-
Notifications
You must be signed in to change notification settings - Fork 415
feat(examples): calculator realm #4084
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
Merged
Merged
Changes from 7 commits
Commits
Show all changes
53 commits
Select commit
Hold shift + click to select a range
9b9492a
feat: add calculator and home realm
NicolasMelet 354b409
feat: changed txlink for path querry, compute still in progress
NicolasMelet 69c4861
feat: computation now works again, app fully handled with path
NicolasMelet e330d9c
clean: removed useless variable and added documentation
NicolasMelet 345cc21
feat: added unit tests
NicolasMelet 8cd3d2a
fix: changed panick errors to standart error handling for evaluateVal…
NicolasMelet 44cb487
feat: parenthesis now available
NicolasMelet f7ebebc
feat: handle negative values + added tests
NicolasMelet c7b7ad9
Merge branch 'master' into master
NicolasMelet 7787495
fix: unexport render functions
NicolasMelet 9aaa711
clean: removed newlines in tests
NicolasMelet ffeae14
clean: use out string instead of strings builder
NicolasMelet def6b5c
clean: use md package for markdown
NicolasMelet 09209d1
Merge branch 'master' into master
NicolasMelet c566b19
Merge branch 'master' into master
leohhhn e10b516
clean: used mdtable package
NicolasMelet 7df0d90
chore: remove home realm
NicolasMelet d529ff5
Merge branch 'master' into master
NicolasMelet 8a1f4e7
Merge branch 'master' into master
NicolasMelet c34cbf4
fix: parenthesis can appear in url + cleanup table making in code
NicolasMelet 99fd13f
Merge branch 'master' into master
NicolasMelet 2749424
Merge branch 'master' into master
NicolasMelet d216360
Merge branch 'master' into master
NicolasMelet ffeda4b
Merge branch 'master' into master
NicolasMelet e2ddb3c
Merge branch 'master' into master
NicolasMelet cda0688
Merge branch 'master' into master
NicolasMelet fee4a6f
Merge branch 'master' into master
NicolasMelet 058d4f8
Merge branch 'master' into master
NicolasMelet deb7d5a
Merge branch 'master' into master
NicolasMelet 6303429
Merge branch 'master' into master
NicolasMelet c447db5
Merge branch 'master' into master
NicolasMelet 4d7c6b1
Merge branch 'master' into master
thehowl 3fbcf59
Merge branch 'master' into master
NicolasMelet fa36992
Merge branch 'master' into master
NicolasMelet 0f97e13
clean: ran gno mod tidy
NicolasMelet 19ee056
fix: tests with parenthesis, added cross for hor
NicolasMelet 37bd076
Merge branch 'master' into master
NicolasMelet 817f337
clean: remove comment
NicolasMelet 09b6d4b
Merge branch 'master' into master
NicolasMelet 464f8f4
Merge branch 'master' into master
NicolasMelet dab8ba6
Merge branch 'master' into master
NicolasMelet 1705341
Merge branch 'master' into master
thehowl 81ba841
Merge branch 'master' into master
NicolasMelet 4d9258f
fix: character '+' is now used instead of 'p'
NicolasMelet 0b89bf7
fix: tests now use '+' instead of 'p'
NicolasMelet 0b875a2
Merge branch 'master' into master
NicolasMelet 0461375
Merge branch 'master' into master
NicolasMelet e7f7958
Merge branch 'master' into master
NicolasMelet 7a1740f
Merge branch 'master' into master
NicolasMelet 8d821e2
Merge branch 'master' into master
NicolasMelet 86e146d
fix: new syntax for hor.Register
NicolasMelet 491e318
Merge branch 'master' into master
thehowl 622a13a
fixup
thehowl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
package calculator | ||
|
||
import ( | ||
"strconv" | ||
"strings" | ||
|
||
"gno.land/p/demo/ufmt" | ||
"gno.land/p/moul/realmpath" | ||
"gno.land/r/leon/hof" | ||
) | ||
|
||
type Node struct { | ||
value string // Value of the current node | ||
left *Node | ||
right *Node | ||
} | ||
|
||
const ( | ||
specialCharacters = "p-*/." | ||
topPriority = "*/" | ||
lowPriority = "p-" | ||
|
||
realmPath = "/r/miko/calculator" | ||
) | ||
|
||
var ( | ||
val float64 | ||
displayVal string | ||
|
||
operationMap = map[string]func(left float64, right float64) float64{ | ||
"p": func(left float64, right float64) float64 { return left + right }, | ||
"-": func(left float64, right float64) float64 { return left - right }, | ||
"*": func(left float64, right float64) float64 { return left * right }, | ||
"/": func(left float64, right float64) float64 { | ||
if right == 0 { | ||
panic("Division by 0 is forbidden") | ||
} | ||
return left / right | ||
}, | ||
} | ||
) | ||
|
||
func init() { | ||
hof.Register("Miko's calculator", "Let's do maths") | ||
} | ||
|
||
func evaluateValidity(line string) (bool, string) { | ||
if len(line) == 0 { | ||
return false, "Invalid empty input" | ||
} // edge case empty line | ||
if strings.Index(specialCharacters, string(line[0])) != -1 || | ||
strings.Index(specialCharacters, string(line[len(line)-1])) != -1 { | ||
return false, "Invalid equation" | ||
} // edge case special character at begining or end | ||
|
||
isPriorSpecial := false | ||
countParenthesis := 0 | ||
|
||
for i := 0; i < len(line); i++ { | ||
if line[i] == '<' { | ||
countParenthesis += 1 | ||
continue | ||
} | ||
if line[i] == '>' { | ||
if isPriorSpecial == true { | ||
return false, "Invalid equation" | ||
} | ||
countParenthesis -= 1 | ||
isPriorSpecial = false | ||
continue | ||
} | ||
if strings.Index(specialCharacters, string(line[i])) != -1 { | ||
if isPriorSpecial { | ||
return false, "Invalid equation" | ||
} | ||
isPriorSpecial = true | ||
continue | ||
} | ||
if line[i] != 'p' && (line[i] < '0' || line[i] > '9') { | ||
return false, "Invalid character encountered " | ||
} | ||
isPriorSpecial = false | ||
} | ||
|
||
if countParenthesis != 0 { | ||
return false, "Invalid equation" | ||
} | ||
println(countParenthesis) | ||
return true, "" | ||
} | ||
|
||
func searchForPriority(priorityList string, line string) *Node { | ||
// for i := 0; i < len(priorityList); i++ { | ||
// idx := strings.Index(line, string(priorityList[i])) | ||
// if idx != -1 { | ||
// return &Node{string(line[idx]), createTree(line[:idx]), createTree(line[idx+1:])} | ||
// } | ||
// } | ||
|
||
countParenthesis := 0 | ||
for iPrio := 0; iPrio < len(priorityList); iPrio++ { | ||
for idx := 0; idx < len(line); idx++ { | ||
if line[idx] == '<' { | ||
countParenthesis += 1 | ||
} | ||
if line[idx] == '>' { | ||
countParenthesis -= 1 | ||
} | ||
if countParenthesis == 0 && line[idx] == priorityList[iPrio] { | ||
println("seen operator") | ||
return &Node{string(line[idx]), createTree(line[:idx]), createTree(line[idx+1:])} | ||
} | ||
|
||
} | ||
} | ||
return nil | ||
} | ||
|
||
func createTree(line string) *Node { | ||
println(line) | ||
if line[0] == '<' && line[len(line)-1] == '>' && strings.Index(line[1:], "<") == -1 { | ||
println("no parenthesis anymore") | ||
return createTree(line[1 : len(line)-1]) | ||
} | ||
node := searchForPriority(lowPriority, line) // we put the lowest priority at the top of the tree, these operations will be executed last | ||
if node != nil { | ||
return node | ||
} | ||
node = searchForPriority(topPriority, line) | ||
if node != nil { | ||
return node | ||
} | ||
|
||
// if this code is reached, the only value possible in line is a number | ||
return &Node{line, nil, nil} | ||
} | ||
|
||
func readTree(tree *Node) float64 { | ||
operation, exists := operationMap[tree.value] | ||
|
||
if exists { // check if the current node is an operator | ||
return operation(readTree(tree.left), readTree(tree.right)) | ||
} | ||
|
||
parsedValue, _ := strconv.ParseFloat(tree.value, 64) | ||
|
||
return parsedValue | ||
} | ||
|
||
// expression is the equation you want to solve (p replaces the + symbol) | ||
// exemple: 2p4/2 | ||
func ComputeResult(expression string) string { | ||
valid, errString := evaluateValidity(expression) | ||
|
||
if !valid { // If a basic error is encountered, return the expression without the = at the end and display the same expression | ||
println(errString) // display error for debug | ||
displayVal = strings.Replace(expression, "p", "+", -1) | ||
displayVal = strings.Replace(displayVal, "<", "(", -1) | ||
displayVal = strings.Replace(displayVal, ">", ")", -1) | ||
return expression | ||
} | ||
|
||
tree := createTree(expression) | ||
|
||
val = readTree(tree) | ||
displayVal = strconv.FormatFloat(val, 'g', 6, 64) | ||
return displayVal | ||
} | ||
|
||
func removeLast(path string) string { | ||
lenPath := len(path) | ||
if lenPath > 0 { | ||
path = path[:lenPath-1] | ||
} | ||
return path | ||
} | ||
|
||
func Render(path string) string { | ||
var sb strings.Builder | ||
leohhhn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
req := realmpath.Parse(path) | ||
query := req.Query | ||
expression := query.Get("expression") | ||
|
||
if expression == "" { | ||
displayVal = "0" | ||
} else { | ||
if expression[len(expression)-1] == '=' { | ||
expression = ComputeResult(expression[:len(expression)-1]) | ||
} else { | ||
displayVal = strings.Replace(expression, "p", "+", -1) | ||
displayVal = strings.Replace(displayVal, "<", "(", -1) | ||
displayVal = strings.Replace(displayVal, ">", ")", -1) | ||
} | ||
} | ||
|
||
sb.WriteString(`# Calculator page | ||
|
||
Have you ever wanted to do maths but never actually found a calculator ? | ||
Do I have the realm for you... | ||
leohhhn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Result: ` + displayVal + ` | ||
--------------- | ||
| ` + ufmt.Sprintf("[res](%s)", realmPath) + `| ` + ufmt.Sprintf("[(](%s)", realmPath+":?expression="+expression+"<") + `| ` + ufmt.Sprintf("[)](%s)", realmPath+":?expression="+expression+">") + `| ` + ufmt.Sprintf("[del](%s)", realmPath+":?expression="+removeLast(expression)) + `| | ||
|---|---|---|---| | ||
| ` + ufmt.Sprintf("[7](%s)", realmPath+":?expression="+expression+"7") + `| ` + ufmt.Sprintf("[8](%s)", realmPath+":?expression="+expression+"8") + `| ` + ufmt.Sprintf("[9](%s)", realmPath+":?expression="+expression+"9") + `| ` + ufmt.Sprintf("[+](%s)", realmPath+":?expression="+expression+"p") /* here p replaces + because of how + works in bnormal paths*/ + `| | ||
| ` + ufmt.Sprintf("[4](%s)", realmPath+":?expression="+expression+"4") + `| ` + ufmt.Sprintf("[5](%s)", realmPath+":?expression="+expression+"5") + `| ` + ufmt.Sprintf("[6](%s)", realmPath+":?expression="+expression+"6") + `| ` + ufmt.Sprintf("[-](%s)", realmPath+":?expression="+expression+"-") + `| | ||
| ` + ufmt.Sprintf("[1](%s)", realmPath+":?expression="+expression+"1") + `| ` + ufmt.Sprintf("[2](%s)", realmPath+":?expression="+expression+"2") + `| ` + ufmt.Sprintf("[3](%s)", realmPath+":?expression="+expression+"3") + `| ` + ufmt.Sprintf("[*](%s)", realmPath+":?expression="+expression+"*") + `| | ||
| ` + ufmt.Sprintf("[0](%s)", realmPath+":?expression="+expression+"0") + `| ` + ufmt.Sprintf("[.](%s)", realmPath+":?expression="+expression+".") + `| ` + ufmt.Sprintf("[=](%s)", realmPath+":?expression="+expression+"=") + `| ` + ufmt.Sprintf("[/](%s)", realmPath+":?expression="+expression+"/") + `| | ||
`) | ||
leohhhn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return sb.String() | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
package calculator | ||
|
||
import "testing" | ||
|
||
func TestCounter_Addition(t *testing.T) { | ||
|
||
leohhhn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// Increment the value | ||
value := ComputeResult("1p1") | ||
|
||
// Verify the value is equal to 2 | ||
if value != "2" { | ||
t.Fatalf("1 + 1 is not equal to 2") | ||
} | ||
} | ||
|
||
func TestCounter_Subtraction(t *testing.T) { | ||
|
||
// Increment the value | ||
value := ComputeResult("1-1") | ||
|
||
// Verify the value is equal to 0 | ||
if value != "0" { | ||
t.Fatalf("1 - 1 is not equal to 0") | ||
} | ||
} | ||
|
||
func TestCounter_Multiplication(t *testing.T) { | ||
|
||
// Increment the value | ||
value := ComputeResult("1*4") | ||
|
||
// Verify the value is equal to 4 | ||
if value != "4" { | ||
t.Fatalf("1 * 4 is not equal to 4") | ||
} | ||
} | ||
|
||
func TestCounter_Division(t *testing.T) { | ||
|
||
// Increment the value | ||
value := ComputeResult("4/2") | ||
|
||
// Verify the value is equal to 2 | ||
if value != "2" { | ||
t.Fatalf("4 / 2 is not equal to 2") | ||
} | ||
} | ||
|
||
func TestCounter_AdditionDecimal(t *testing.T) { | ||
|
||
// Increment the value | ||
value := ComputeResult("1.2p1.3") | ||
|
||
// Verify the value is equal to 2.5 | ||
if value != "2.5" { | ||
t.Fatalf("1.2 + 1.3 is not equal to 2.5") | ||
} | ||
} | ||
|
||
func TestCounter_SubtractionDecimal(t *testing.T) { | ||
|
||
// Increment the value | ||
value := ComputeResult("1.3-1.2") | ||
|
||
// Verify the value is equal to 0.1 | ||
if value != "0.1" { | ||
t.Fatalf("1.3 - 1.2 is not equal to 0.1") | ||
} | ||
} | ||
|
||
func TestCounter_MultiplicationDecimal(t *testing.T) { | ||
|
||
// Increment the value | ||
value := ComputeResult("3*1.5") | ||
|
||
// Verify the value is equal to 4.5 | ||
if value != "4.5" { | ||
t.Fatalf("3 * 1.5 is not equal to 4.5") | ||
} | ||
} | ||
|
||
func TestCounter_DivisionDecimal(t *testing.T) { | ||
|
||
// Increment the value | ||
value := ComputeResult("2/0.5") | ||
|
||
// Verify the value is equal to 4 | ||
if value != "4" { | ||
t.Fatalf("2 / 0.5 is not equal to 4") | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module gno.land/r/miko/calculator |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module gno.land/r/miko/home |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package home | ||
|
||
import ( | ||
"gno.land/p/demo/ufmt" | ||
) | ||
|
||
var count int | ||
|
||
// this here serves to increment my counter | ||
func Increment() { | ||
count++ | ||
} | ||
|
||
func Decrement() { | ||
count-- | ||
} | ||
|
||
func init() { | ||
count = 0 // arbitrary value | ||
} | ||
|
||
func Render(_ string) string { | ||
out := "# Hey !\n" | ||
out += "My name is Nicolas, french student in computer science !\n\n" | ||
out += "Want to hear about me ?\n\n" | ||
out += RenderPassion() | ||
out += RenderCounter() | ||
return out | ||
} | ||
|
||
func RenderPassion() string { | ||
leohhhn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
out := "## Passions\n\n" | ||
out += "### I love:\n\n" | ||
out += "Crying about Hollow knight silksong never coming out\n\n" | ||
out += "" | ||
out += "\n\n" | ||
out += "Playing the award winning MMORPG Final Fantasy XIV online\n\n" | ||
out += "And when I'm not doing any of the above, I like coding in C/C++ ~( °v°)\n" | ||
return out | ||
} | ||
|
||
func RenderCounter() string { | ||
out := "## Secret counter\n\n" | ||
out += "No one really knows what this counter actually counts\n\n" | ||
out += "**" + ufmt.Sprintf("%d", count) + "**" | ||
out += "\n\n\n... but it really ain't that high\n\n" | ||
return out | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.