You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

139 lines
3.2 KiB
Go

2 years ago
package script
import (
"os"
"github.com/alecthomas/participle/v2"
"github.com/alecthomas/participle/v2/lexer"
)
type Program struct {
Statements []Expression `parser:"Newline? ( @@ ( Newline @@ )* Newline? )?"`
}
type ArgumentExpression interface{}
type Expression interface{}
type FunctionReceiver interface{}
var _ FunctionReceiver = &Identifier{}
var _ FunctionReceiver = &ParenthesizedExpression{}
type FunctionCall struct {
Receiver FunctionReceiver `parser:"@@"`
Arguments []ArgumentExpression `parser:"@@+"`
}
type ParenthesizedExpression struct {
Inner Expression `parser:"'(' @@ ')'"`
}
type ListExpression struct {
Values []Expression `parser:"'[' ( Newline? @@ ( Newline? @@ )* Newline? )? ']'"`
}
type DictExpression struct {
Entries []*DictEntry `parser:"'{' ( Newline? @@ ( ( ',' | Newline ) @@ )* Newline? )? '}'"`
}
type DictEntry struct {
Key DictEntryKey `parser:"@@"`
Value Expression `parser:"'=' @@"`
}
type DictEntryKey interface{}
type Boolean bool
func (b *Boolean) Capture(values []string) error {
*b = values[0] == "true"
return nil
}
type LiteralBoolean struct {
Value Boolean `parser:"@('true' | 'false')"`
}
type LiteralString struct {
Value string `parser:"@String"`
}
type LiteralNumber struct {
Value float64 `parser:"@Number"`
}
type Identifier struct {
Value string `parser:"@Ident"`
}
type Quote struct {
Value Quotation `parser:"'#' @@"`
}
type Quotation interface{}
type ExpressionFactor interface{}
type BinaryOperation struct {
Lhs ExpressionFactor `parser:"@@"`
Operator string `parser:"( @Operator"`
Rhs Expression `parser:" @@ )?"`
}
var (
scriptLexer = lexer.MustSimple([]lexer.SimpleRule{
{Name: "Comment", Pattern: `--[^\n]*\n?`},
{Name: "String", Pattern: `"(\\"|[^"])*"`},
{Name: "Number", Pattern: `[-+]?(?:\d*\.)?\d+`},
{Name: "Ident", Pattern: `[a-zA-Z]\w*`},
{Name: "Meta", Pattern: `\#`},
// an operator is any combination of [+-*/%<>=:;,&|^?#@] expect for the string "="
{Name: "Operator", Pattern: `(=[\+\-\*/%<>=:&|\^?#@]+)|([\+\-\*/%<>:&|\^?#@][\+\-\*/%<>=:&|\^?#@]*)`},
{Name: "Punct", Pattern: `[\(\)\{\}\[\]=,]`},
{Name: "Newline", Pattern: `[ \t]*\n\s*`},
{Name: "Whitespace", Pattern: `[ \t]+`},
})
// Parser
Parser = participle.MustBuild[Program](
participle.Lexer(scriptLexer),
participle.Elide("Comment", "Whitespace"),
participle.Unquote("String"),
participle.Union[Expression](
&FunctionCall{},
&BinaryOperation{},
),
participle.Union[ArgumentExpression](
&BinaryOperation{},
),
participle.Union[ExpressionFactor](
&ListExpression{},
&DictExpression{},
&LiteralNumber{},
&LiteralString{},
&LiteralBoolean{},
&ParenthesizedExpression{},
&Quote{},
&Identifier{},
),
participle.Union[Quotation](
&Identifier{},
&ParenthesizedExpression{},
),
participle.Union[DictEntryKey](
&Identifier{},
&ParenthesizedExpression{},
),
participle.Union[FunctionReceiver](
&Identifier{},
&ParenthesizedExpression{},
),
participle.UseLookahead(2),
)
)
func Parse(source string) (*Program, error) {
return Parser.ParseString("", source, participle.Trace(os.Stdout))
// return Parser.ParseString("", source)
}