Initial Commit
This commit is contained in:
201
internal/ast/ast.go
Normal file
201
internal/ast/ast.go
Normal file
@@ -0,0 +1,201 @@
|
||||
package ast
|
||||
|
||||
import "fmt"
|
||||
|
||||
func PosError(n Node, filename, format string, v ...any) error {
|
||||
return fmt.Errorf(filename+":"+n.Pos().String()+": "+format, v...)
|
||||
}
|
||||
|
||||
type Node interface {
|
||||
Pos() Position
|
||||
}
|
||||
|
||||
type Position struct {
|
||||
Line int
|
||||
Col int
|
||||
}
|
||||
|
||||
func (p Position) String() string {
|
||||
return fmt.Sprintf("%d:%d", p.Line, p.Col)
|
||||
}
|
||||
|
||||
type Tag struct {
|
||||
Name Ident
|
||||
Params []Node
|
||||
HasBody bool
|
||||
Position Position
|
||||
}
|
||||
|
||||
func (t Tag) Pos() Position {
|
||||
return t.Position
|
||||
}
|
||||
|
||||
type ExprTag struct {
|
||||
Value Node
|
||||
Position Position
|
||||
}
|
||||
|
||||
func (et ExprTag) Pos() Position {
|
||||
return et.Position
|
||||
}
|
||||
|
||||
type EndTag struct {
|
||||
Name Ident
|
||||
Position Position
|
||||
}
|
||||
|
||||
func (et EndTag) Pos() Position {
|
||||
return et.Position
|
||||
}
|
||||
|
||||
type Text struct {
|
||||
Data []byte
|
||||
Position Position
|
||||
}
|
||||
|
||||
func (t Text) Pos() Position {
|
||||
return t.Position
|
||||
}
|
||||
|
||||
type Value struct {
|
||||
Node
|
||||
Not bool
|
||||
}
|
||||
|
||||
type Expr struct {
|
||||
Segment Node
|
||||
Logical Logical
|
||||
Rest []Expr
|
||||
Position Position
|
||||
}
|
||||
|
||||
func (e Expr) Pos() Position {
|
||||
return e.Position
|
||||
}
|
||||
|
||||
type ExprSegment struct {
|
||||
Value Node
|
||||
Operator Operator
|
||||
Rest []ExprSegment
|
||||
Position Position
|
||||
}
|
||||
|
||||
func (es ExprSegment) Pos() Position {
|
||||
return es.Position
|
||||
}
|
||||
|
||||
type FuncCall struct {
|
||||
Name Ident
|
||||
Params []Node
|
||||
Position Position
|
||||
}
|
||||
|
||||
func (fc FuncCall) Pos() Position {
|
||||
return fc.Position
|
||||
}
|
||||
|
||||
type MethodCall struct {
|
||||
Value Node
|
||||
Name Ident
|
||||
Params []Node
|
||||
Position Position
|
||||
}
|
||||
|
||||
func (mc MethodCall) Pos() Position {
|
||||
return mc.Position
|
||||
}
|
||||
|
||||
type FieldAccess struct {
|
||||
Value Node
|
||||
Name Ident
|
||||
Position Position
|
||||
}
|
||||
|
||||
func (fa FieldAccess) Pos() Position {
|
||||
return fa.Position
|
||||
}
|
||||
|
||||
type Index struct {
|
||||
Value Node
|
||||
Index Node
|
||||
Position Position
|
||||
}
|
||||
|
||||
func (i Index) Pos() Position {
|
||||
return i.Position
|
||||
}
|
||||
|
||||
type Ident struct {
|
||||
Value string
|
||||
Position Position
|
||||
}
|
||||
|
||||
func (id Ident) Pos() Position {
|
||||
return id.Position
|
||||
}
|
||||
|
||||
type String struct {
|
||||
Value string
|
||||
Position Position
|
||||
}
|
||||
|
||||
func (s String) Pos() Position {
|
||||
return s.Position
|
||||
}
|
||||
|
||||
type Float struct {
|
||||
Value float64
|
||||
Position Position
|
||||
}
|
||||
|
||||
func (f Float) Pos() Position {
|
||||
return f.Position
|
||||
}
|
||||
|
||||
type Integer struct {
|
||||
Value int64
|
||||
Position Position
|
||||
}
|
||||
|
||||
func (i Integer) Pos() Position {
|
||||
return i.Position
|
||||
}
|
||||
|
||||
type Bool struct {
|
||||
Value bool
|
||||
Position Position
|
||||
}
|
||||
|
||||
func (b Bool) Pos() Position {
|
||||
return b.Position
|
||||
}
|
||||
|
||||
type Op interface {
|
||||
Op() string
|
||||
}
|
||||
|
||||
type Operator struct {
|
||||
Value string
|
||||
Position Position
|
||||
}
|
||||
|
||||
func (op Operator) Pos() Position {
|
||||
return op.Position
|
||||
}
|
||||
|
||||
func (op Operator) Op() string {
|
||||
return op.Value
|
||||
}
|
||||
|
||||
type Logical struct {
|
||||
Value string
|
||||
Position Position
|
||||
}
|
||||
|
||||
func (l Logical) Pos() Position {
|
||||
return l.Position
|
||||
}
|
||||
|
||||
func (l Logical) Op() string {
|
||||
return l.Value
|
||||
}
|
||||
3
internal/parser/gen.go
Normal file
3
internal/parser/gen.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package parser
|
||||
|
||||
//go:generate pigeon --support-left-recursion -o parser.go salix.peg
|
||||
2713
internal/parser/parser.go
Normal file
2713
internal/parser/parser.go
Normal file
File diff suppressed because it is too large
Load Diff
220
internal/parser/salix.peg
Normal file
220
internal/parser/salix.peg
Normal file
@@ -0,0 +1,220 @@
|
||||
{
|
||||
package parser
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"go.elara.ws/salix/internal/ast"
|
||||
)
|
||||
|
||||
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{
|
||||
Line: c.pos.line,
|
||||
Col: c.pos.col,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
ExprTag = "#(" expr:Expr ')' {
|
||||
return ast.ExprTag{
|
||||
Value: expr.(ast.Node),
|
||||
Position: getPos(c),
|
||||
}, nil
|
||||
}
|
||||
|
||||
Expr = first:ExprSegment rest:(_ Logical _ ExprSegment)* {
|
||||
restSlice := toAnySlice(rest)
|
||||
if len(restSlice) == 0 {
|
||||
return first, nil
|
||||
}
|
||||
|
||||
out := ast.Expr{Segment: first.(ast.Node), Position: getPos(c)}
|
||||
for _, restValue := range restSlice {
|
||||
valueSlice := toAnySlice(restValue)
|
||||
out.Rest = append(out.Rest, ast.Expr{
|
||||
Logical: valueSlice[1].(ast.Logical),
|
||||
Segment: valueSlice[3].(ast.Node),
|
||||
Position: valueSlice[3].(ast.Node).Pos(),
|
||||
})
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
ExprSegment = first:Value rest:(_ Operator _ Value)* {
|
||||
restSlice := toAnySlice(rest)
|
||||
if len(restSlice) == 0 {
|
||||
return first, nil
|
||||
}
|
||||
|
||||
out := ast.ExprSegment{Value: first.(ast.Node), Position: getPos(c)}
|
||||
for _, restValue := range restSlice {
|
||||
valueSlice := toAnySlice(restValue)
|
||||
out.Rest = append(out.Rest, ast.ExprSegment{
|
||||
Operator: valueSlice[1].(ast.Operator),
|
||||
Value: valueSlice[3].(ast.Node),
|
||||
Position: valueSlice[3].(ast.Node).Pos(),
|
||||
})
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
ParamList = '(' params:(Expr ( ',' _ Expr )* )? ')' {
|
||||
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
|
||||
}
|
||||
|
||||
Value = not:"!"? node:(MethodCall / FieldAccess / Index / String / RawString / Float / Integer / Bool / FuncCall / Ident) {
|
||||
return ast.Value{
|
||||
Node: node.(ast.Node),
|
||||
Not: not != nil,
|
||||
}, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
Integer = ("0x" [0-9a-f]i+ / "0o" [0-7]+ / "0b" [01]+ / [0-9]+) {
|
||||
i, err := strconv.ParseInt(string(c.text), 0, 64)
|
||||
return ast.Integer{
|
||||
Value: i,
|
||||
Position: getPos(c),
|
||||
}, err
|
||||
}
|
||||
|
||||
Float = value:([0-9]+ '.' [0-9]+) {
|
||||
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
|
||||
}
|
||||
|
||||
Operator = ("==" / "<=" / ">=" / "in"i / '<' / '>' / '+' / '-' / '/' / '*') {
|
||||
return ast.Operator{
|
||||
Value: string(c.text),
|
||||
Position: getPos(c),
|
||||
}, nil
|
||||
}
|
||||
|
||||
Logical = ("&&" / "||") {
|
||||
return ast.Logical{
|
||||
Value: string(c.text),
|
||||
Position: getPos(c),
|
||||
}, nil
|
||||
}
|
||||
|
||||
Text = . [^#]* { return ast.Text{Data: c.text, Position: getPos(c)}, nil }
|
||||
|
||||
_ "whitespace" ← [ \t\r\n]*
|
||||
Reference in New Issue
Block a user