Initial Commit

This commit is contained in:
2023-10-28 18:10:17 -07:00
commit 71a09170c9
20 changed files with 4991 additions and 0 deletions

201
internal/ast/ast.go Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

220
internal/parser/salix.peg Normal file
View 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]*