diff --git a/expression.go b/expression.go index 0e7102f..401720e 100644 --- a/expression.go +++ b/expression.go @@ -235,6 +235,11 @@ type Symbol struct { expr Expression } +type Param struct { + id *Identifier + t Type +} + func (v *Symbol) Step() (Expression, error) { if v.expr == nil { return nil, fmt.Errorf("unbound symbol %s at %s", v.id, v.id.position) diff --git a/input.go b/input.go index 4d9c012..3421a0d 100644 --- a/input.go +++ b/input.go @@ -42,12 +42,12 @@ func (in *Input) Seek(i int) { in.index = i } -func (in *Input) Take(amount int) *Input { +func (in *Input) Take(offset int) *Input { var out = &Input{ index: 0, - input: in.input[in.index : in.index+amount], + input: in.input[in.index : in.index+offset], } - in.index += amount + in.index += offset return out } @@ -70,3 +70,27 @@ func (in *Input) Find(open, close *Token) (int, bool) { return 0, false } } + +func (in *Input) TakeParens(open, close *Token) (*Input, error) { + if in.input[in.index].Equals(open) { + return nil, noMatch + } + start := in.index + level := 1 + idx := slices.IndexFunc(in.input[start+1:], func(a Lexeme) bool { + switch { + case a.Equals(open): + level++ + case a.Equals(close): + level-- + return level == 0 + } + return false + }) + if idx > -1 { + _ = in.Take(idx + 1) + return &Input{index: 0, input: in.input[start+1 : idx]}, nil + } else { + return nil, fmt.Errorf("unmatched parenthesis starting at %s", in.Peek(start).Position()) + } +} diff --git a/lex.go b/lex.go index d07f765..87b4529 100644 --- a/lex.go +++ b/lex.go @@ -22,40 +22,44 @@ func (p *position) Position() position { return *p } -type Range [2]position +type ( + Range [2]position -type token[T comparable] struct { - position - value T -} + token[T comparable] struct { + position + value T + } -type Lexeme interface { - Position() position - Equals(Lexeme) bool -} + Lexeme interface { + Position() position + Equals(Lexeme) bool + } -type Id string + Id string -type Token token[string] + Token token[string] -type IntToken token[int] + IntToken token[int] -type FloatToken token[float64] + FloatToken token[float64] -type Identifier token[Id] + Identifier token[Id] +) var ( - Lparen = &Token{value: "("} - Rparen = &Token{value: ")"} - Space = &Token{value: " "} - Tab = &Token{value: "\t"} - Newline = &Token{value: "\n"} - SingleQuote = &Token{value: "'"} - Let = &Token{value: "let"} - LambdaTok = &Token{value: "lambda"} - If = &Token{value: "if"} - TrueTok = &Token{value: "t"} - NilTok = &Token{value: "nil"} + Lparen = &Token{value: "("} + Rparen = &Token{value: ")"} + LsquareBracket = &Token{value: "["} + RsquareBracket = &Token{value: "]"} + Space = &Token{value: " "} + Tab = &Token{value: "\t"} + Newline = &Token{value: "\n"} + SingleQuote = &Token{value: "'"} + Let = &Token{value: "let"} + LambdaTok = &Token{value: "lambda"} + If = &Token{value: "if"} + TrueTok = &Token{value: "t"} + NilTok = &Token{value: "nil"} ) func (t Token) String() string { @@ -107,7 +111,7 @@ func (t *Identifier) Equals(a Lexeme) bool { var matchInt = regexp.MustCompile(`-?\d+`) var matchFloat = regexp.MustCompile(`-?\d+\.\d*`) -var matchIdentifier = regexp.MustCompile(`[^\'() ]+`) +var matchIdentifier = regexp.MustCompile(`[^\'() \[\]]+`) func lex(source string) (*Input, error) { var ( diff --git a/main.go b/main.go index de680d2..6551db5 100644 --- a/main.go +++ b/main.go @@ -28,18 +28,27 @@ func fullStep(e Expression) (Expression, error) { func builtinFunctions(expr Expression) { functions := map[Id]BuiltInFunction{ - // "quote": { - // t: &FunctionType{ - // ret: &PrimInteger, - // params: , - // }, - // f: func(e []Expression) (Expression, error) { - // if len(e) == 1 { - // return &Quoted{e[0]}, nil - // } - // return &Quoted{&List{e}}, nil - // }, - // }, + "+.": { + t: &FunctionType{ + ret: &PrimFloat, + params: []Type{&PrimFloat, &PrimFloat}, + }, + f: func(args []Expression) (Expression, error) { + var sum FloatValue = 0 + for _, arg := range args { + if !isValue(arg) { + panic("not a value") + } + switch x := arg.(type) { + case *FloatValue: + sum += *x + default: + panic("type error") + } + } + return &sum, nil + }, + }, "+": { t: &FunctionType{ ret: &PrimInteger, @@ -61,27 +70,27 @@ func builtinFunctions(expr Expression) { return &sum, nil }, }, - // "-": func(args []Expression) (Expression, error) { - // v0, ok := args[0].(*IntValue) - // if !ok { - // return nil, fmt.Errorf("integer value was expected") - // } - // v1, ok := args[1].(*IntValue) - // if !ok { - // return nil, fmt.Errorf("integer value was expected") - // } - // var result IntValue = (*v0 - *v1) - // return &result, nil - // }, - // "=": func(args []Expression) (Expression, error) { - // if reflect.TypeOf(args[0]) != reflect.TypeOf(args[1]) { - // return nil, fmt.Errorf("unmatched types") - // } - // v0 := reflect.ValueOf(args[0]) - // v1 := reflect.ValueOf(args[1]) - // var result BoolValue = BoolValue(v0.Equal(v1)) - // return &result, nil - // }, + "-": { + t: &FunctionType{ + ret: &PrimInteger, + params: []Type{&PrimInteger, &PrimInteger}, + }, + f: func(args []Expression) (Expression, error) { + var sum IntValue = 0 + for _, arg := range args { + if !isValue(arg) { + panic("not a value") + } + switch x := arg.(type) { + case *IntValue: + sum += *x + default: + panic("type error") + } + } + return &sum, nil + }, + }, } for id, f := range functions { expr.Substitute(id, &f) diff --git a/parse.go b/parse.go index e743f60..69ad500 100644 --- a/parse.go +++ b/parse.go @@ -96,6 +96,35 @@ func parseValue(in *Input) (Expression, error) { } } +func parseParamList(in *Input) (Expression, error) { + list, err := in.TakeParens(Lparen, Rparen) + if err != nil { + return nil, err + } + var params []*Symbol + for list.Len() > 0 { + p, err := list.TakeParens(LsquareBracket, RsquareBracket) + if err != nil { + return nil, err + } + if p.Len() != 2 { + return nil, fmt.Errorf("expected parameter in the following form: [identifier type]") + } + id, ok := p.Peek(0).(*Identifier) + if !ok { + return nil, noMatch + } + typ, ok := p.Peek(1).(*Identifier) + if !ok { + return nil, noMatch + } + params = append(params, &Symbol{ + id: , + }) + } + return args, nil +} + func parseList(in *Input) (Expression, error) { openingParens, ok := in.Pop() if !ok { @@ -192,11 +221,7 @@ func parseLambda(in *Input) (Expression, error) { return nil, fmt.Errorf("unmatched parenthesis starting at %s", openingParens.Position()) } decl := in.Take(idx) - tok, ok := decl.Pop() - if !ok { - return nil, noMatch - } - if !tok.Equals(LambdaTok) { + if tok, ok := decl.Pop(); !ok || !tok.Equals(LambdaTok) { return nil, noMatch } l, err := parseList(decl) diff --git a/type.go b/type.go index 1ea392c..374e084 100644 --- a/type.go +++ b/type.go @@ -84,6 +84,12 @@ func (fa *FunctionType) Equal(b Type) bool { return fa.ret.Equal(fb.ret) } +// func FromId(id Id) (Type, error) { +// switch id { +// case "integer": +// } +// } + // func (fa *VariadicListType) Equal(b Type) bool { // return false // }