2023-10-29 01:10:17 +00:00
|
|
|
{
|
|
|
|
package parser
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strconv"
|
|
|
|
|
2023-10-31 01:38:53 +00:00
|
|
|
"go.elara.ws/salix/ast"
|
2023-10-29 01:10:17 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func toAnySlice(v any) []any {
|
|
|
|
if v == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return v.([]any)
|
|
|
|
}
|
|
|
|
|
|
|
|
func toNodeSlice(v any) []ast.Node {
|
|
|
|
if v == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return v.([]ast.Node)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getPos(c *current) ast.Position {
|
|
|
|
return ast.Position{
|
2023-12-18 00:55:13 +00:00
|
|
|
Name: c.globalStore["name"].(string),
|
2023-10-29 01:10:17 +00:00
|
|
|
Line: c.pos.line,
|
|
|
|
Col: c.pos.col,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-30 22:02:41 +00:00
|
|
|
func toExpr(c *current, first, rest any) ast.Node {
|
|
|
|
restSlice := toAnySlice(rest)
|
|
|
|
if len(restSlice) == 0 {
|
|
|
|
return first.(ast.Node)
|
|
|
|
}
|
|
|
|
|
|
|
|
out := ast.Expr{First: first.(ast.Node), Position: getPos(c)}
|
|
|
|
for _, restValue := range restSlice {
|
|
|
|
valueSlice := toAnySlice(restValue)
|
|
|
|
out.Rest = append(out.Rest, ast.Expr{
|
|
|
|
Operator: valueSlice[1].(ast.Operator),
|
|
|
|
First: valueSlice[3].(ast.Node),
|
|
|
|
Position: valueSlice[3].(ast.Node).Pos(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2023-10-29 01:10:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Root = items:(Tag / ExprTag / EndTag / Text)* {
|
|
|
|
itemSlice := toAnySlice(items)
|
|
|
|
out := make([]ast.Node, len(itemSlice))
|
|
|
|
for i, item := range itemSlice{
|
|
|
|
out[i] = item.(ast.Node)
|
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
Tag = '#' name:Ident params:ParamList? body:':'? {
|
|
|
|
return ast.Tag{
|
|
|
|
Name: name.(ast.Ident),
|
|
|
|
Params: toNodeSlice(params),
|
|
|
|
HasBody: body != nil,
|
|
|
|
Position: getPos(c),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
EndTag = "#!" name:Ident {
|
|
|
|
return ast.EndTag{
|
|
|
|
Name: name.(ast.Ident),
|
|
|
|
Position: getPos(c),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2024-02-08 05:42:43 +00:00
|
|
|
ExprTag = '#' ignoreErr:'?'? '(' item:Expr ')' {
|
2023-10-29 01:10:17 +00:00
|
|
|
return ast.ExprTag{
|
2024-02-08 02:27:21 +00:00
|
|
|
Value: item.(ast.Node),
|
|
|
|
IgnoreError: ignoreErr != nil,
|
|
|
|
Position: getPos(c),
|
2023-10-29 01:10:17 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2024-06-07 01:52:14 +00:00
|
|
|
Expr = Assignment / TernaryExpr
|
|
|
|
Assignable = TernaryExpr
|
|
|
|
|
|
|
|
TernaryExpr = _ cond:LogicalExpr vals:(_ '?' _ Value _ ':' _ Value)? {
|
|
|
|
if vals == nil {
|
|
|
|
return cond, nil
|
|
|
|
} else {
|
|
|
|
s := toAnySlice(vals)
|
|
|
|
return ast.Ternary{
|
|
|
|
Condition: cond.(ast.Node),
|
|
|
|
IfTrue: s[3].(ast.Node),
|
|
|
|
Else: s[7].(ast.Node),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
}
|
2023-10-30 15:37:59 +00:00
|
|
|
|
2023-10-30 22:02:41 +00:00
|
|
|
LogicalExpr = _ first:ComparisonExpr rest:(_ LogicalOp _ ComparisonExpr)* _ {
|
|
|
|
return toExpr(c, first, rest), nil
|
2023-10-29 01:10:17 +00:00
|
|
|
}
|
|
|
|
|
2023-10-30 22:02:41 +00:00
|
|
|
ComparisonExpr = _ first:ArithmeticExpr rest:(_ ComparisonOp _ ArithmeticExpr)* _ {
|
|
|
|
return toExpr(c, first, rest), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
ArithmeticExpr = _ first:Value rest:(_ ArithmeticOp _ Value)* _ {
|
|
|
|
return toExpr(c, first, rest), nil
|
2023-10-29 01:10:17 +00:00
|
|
|
}
|
|
|
|
|
2023-10-30 22:02:41 +00:00
|
|
|
ParenExpr = '(' expr:Expr ')' {
|
|
|
|
return expr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
ParamList = '(' params:(Expr ( ',' _ Expr )* )? ')' {
|
2023-10-29 01:10:17 +00:00
|
|
|
paramSlice := toAnySlice(params)
|
|
|
|
if len(paramSlice) == 0 {
|
|
|
|
return []ast.Node{}, nil
|
|
|
|
}
|
|
|
|
out := []ast.Node{paramSlice[0].(ast.Node)}
|
|
|
|
restSlice := toAnySlice(paramSlice[1])
|
|
|
|
for _, value := range restSlice {
|
|
|
|
valueSlice := toAnySlice(value)
|
|
|
|
out = append(out, valueSlice[2].(ast.Node))
|
|
|
|
}
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
2024-06-07 02:06:45 +00:00
|
|
|
Value = not:"!"? node:(Nil / MethodCall / FieldAccess / Index / String / RawString / Float / Integer / Bool / FuncCall / VariableOr / Ident / ParenExpr / Array / Map) {
|
2023-10-29 01:10:17 +00:00
|
|
|
return ast.Value{
|
|
|
|
Node: node.(ast.Node),
|
|
|
|
Not: not != nil,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2024-03-07 22:06:59 +00:00
|
|
|
Map = '{' _ fpair:(Assignable _ ':' _ Assignable)? _ pairs:(',' _ Assignable _ ':' _ Assignable _)* _ ','? _ '}' {
|
|
|
|
out := ast.Map{
|
|
|
|
Map: map[ast.Node]ast.Node{},
|
|
|
|
Position: getPos(c),
|
|
|
|
}
|
|
|
|
|
|
|
|
fpairSlice := toAnySlice(fpair)
|
|
|
|
if fpairSlice == nil {
|
|
|
|
return out, nil
|
|
|
|
} else {
|
|
|
|
out.Map[fpairSlice[0].(ast.Node)] = fpairSlice[4].(ast.Node)
|
|
|
|
for _, pair := range toAnySlice(pairs) {
|
|
|
|
pairSlice := toAnySlice(pair)
|
|
|
|
out.Map[pairSlice[2].(ast.Node)] = pairSlice[6].(ast.Node)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
Array = '[' _ fval:Assignable? _ vals:(',' _ Assignable _)* ','? _ ']' {
|
|
|
|
out := ast.Array{Position: getPos(c)}
|
|
|
|
|
|
|
|
if fval == nil {
|
|
|
|
return out, nil
|
|
|
|
} else {
|
|
|
|
out.Array = append(out.Array, fval.(ast.Node))
|
|
|
|
for _, val := range toAnySlice(vals) {
|
|
|
|
valSlice := toAnySlice(val)
|
|
|
|
out.Array = append(out.Array, valSlice[2].(ast.Node))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return out, nil
|
|
|
|
}
|
|
|
|
|
2023-10-31 20:45:00 +00:00
|
|
|
VariableOr = variable:Ident _ '|' _ or:Assignable {
|
2023-10-30 15:52:02 +00:00
|
|
|
return ast.VariableOr{
|
|
|
|
Variable: variable.(ast.Ident),
|
|
|
|
Or: or.(ast.Node),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2023-10-31 20:45:00 +00:00
|
|
|
Assignment = name:Ident _ '=' _ value:Assignable {
|
|
|
|
return ast.Assignment{
|
|
|
|
Name: name.(ast.Ident),
|
|
|
|
Value: value.(ast.Node),
|
|
|
|
Position: getPos(c),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2023-10-29 01:10:17 +00:00
|
|
|
MethodCall = value:Value '.' name:Ident params:ParamList {
|
|
|
|
return ast.MethodCall{
|
|
|
|
Value: value.(ast.Node),
|
|
|
|
Name: name.(ast.Ident),
|
|
|
|
Params: toNodeSlice(params),
|
|
|
|
Position: getPos(c),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
Index = value:Value '[' index:Value ']' {
|
|
|
|
return ast.Index{
|
|
|
|
Value: value.(ast.Node),
|
|
|
|
Index: index.(ast.Node),
|
|
|
|
Position: getPos(c),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
FieldAccess = value:Value '.' name:Ident {
|
|
|
|
return ast.FieldAccess{
|
|
|
|
Value: value.(ast.Node),
|
|
|
|
Name: name.(ast.Ident),
|
|
|
|
Position: getPos(c),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
Ident = [a-z]i [a-z0-9_]i* {
|
|
|
|
return ast.Ident{
|
|
|
|
Value: string(c.text),
|
|
|
|
Position: getPos(c),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
FuncCall = name:Ident params:ParamList {
|
|
|
|
return ast.FuncCall{
|
|
|
|
Name: name.(ast.Ident),
|
|
|
|
Params: toNodeSlice(params),
|
|
|
|
Position: getPos(c),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2023-10-30 22:10:55 +00:00
|
|
|
Integer = '-'? ("0x" [0-9a-f]i+ / "0o" [0-7]+ / "0b" [01]+ / [0-9]+) {
|
2023-10-29 01:10:17 +00:00
|
|
|
i, err := strconv.ParseInt(string(c.text), 0, 64)
|
|
|
|
return ast.Integer{
|
|
|
|
Value: i,
|
|
|
|
Position: getPos(c),
|
|
|
|
}, err
|
|
|
|
}
|
|
|
|
|
2023-10-30 22:10:55 +00:00
|
|
|
Float = '-'? value:([0-9]+ '.' [0-9]+) {
|
2023-10-29 01:10:17 +00:00
|
|
|
f, err := strconv.ParseFloat(string(c.text), 64)
|
|
|
|
return ast.Float{
|
|
|
|
Value: f,
|
|
|
|
Position: getPos(c),
|
|
|
|
}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
String = '"' value:[^"]* '"' {
|
|
|
|
s, err := strconv.Unquote(string(c.text))
|
|
|
|
return ast.String{
|
|
|
|
Value: s,
|
|
|
|
Position: getPos(c),
|
|
|
|
}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
RawString = '`' value:[^`]* '`' {
|
|
|
|
s, err := strconv.Unquote(string(c.text))
|
|
|
|
return ast.String{
|
|
|
|
Value: s,
|
|
|
|
Position: getPos(c),
|
|
|
|
}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
Bool = ("true"i / "false"i) {
|
|
|
|
b, err := strconv.ParseBool(string(c.text))
|
|
|
|
return ast.Bool{
|
|
|
|
Value: b,
|
|
|
|
Position: getPos(c),
|
|
|
|
}, err
|
|
|
|
}
|
|
|
|
|
2023-10-30 22:02:41 +00:00
|
|
|
LogicalOp = ("||" / "&&") {
|
|
|
|
return ast.Operator{
|
|
|
|
Value: string(c.text),
|
|
|
|
Position: getPos(c),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2023-12-22 23:41:07 +00:00
|
|
|
ComparisonOp = ("==" / "!=" / "<=" / ">=" / '<' / '>' / "in"i) {
|
2023-10-29 01:10:17 +00:00
|
|
|
return ast.Operator{
|
|
|
|
Value: string(c.text),
|
|
|
|
Position: getPos(c),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2023-10-30 22:02:41 +00:00
|
|
|
ArithmeticOp = ('+' / '-' / '/' / '*' / '%') {
|
|
|
|
return ast.Operator{
|
2023-10-29 01:10:17 +00:00
|
|
|
Value: string(c.text),
|
|
|
|
Position: getPos(c),
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2024-06-07 02:06:45 +00:00
|
|
|
Nil = "nil" {
|
|
|
|
return ast.Nil{Position: getPos(c)}, nil
|
|
|
|
}
|
|
|
|
|
2023-10-29 01:10:17 +00:00
|
|
|
Text = . [^#]* { return ast.Text{Data: c.text, Position: getPos(c)}, nil }
|
|
|
|
|
2024-02-08 02:27:21 +00:00
|
|
|
_ "whitespace" ← [ \t\r\n]*
|