Skip to content
This repository has been archived by the owner on Oct 17, 2024. It is now read-only.

Complete bounty #4 #34

Closed
wants to merge 1 commit into from
Closed

Conversation

grepsuzette
Copy link

No description provided.

@grepsuzette
Copy link
Author

grepsuzette commented Apr 6, 2023

Added an example to my port of joeson.
It's a small lisp vm, it has an REPL.

As I understand you don't necessarily have the time to jump into the code right now I'll provide a small overview here.

The main_test.go is mostly:

package main

import (
    "fmt"
    "testing"
)

type exprExpectation struct {
    expr        string
    expectation string
}

func duo(s string, s2 string) exprExpectation {
    // return exprExpectation{strings.Replace(s, "\n", "", -1), s2}
    return exprExpectation{s, s2}
}

const (
    TRUE  = "1.000000"
    FALSE = "0.000000"
)

var tests = []exprExpectation{
    // ------------ user-defined  -----------------------------
    duo("(define (double x) (+ x x))", "()"),
    duo("(double 15)", "30.000000"),
    duo("(define (quadruple x) (double (double x)))", "()"),
    duo("(quadruple 15)", "60.000000"),
    duo("(define (cadr l) (car (cdr l)))", "()"),
    duo(`(cadr ("a" "b" "c"))`, `"b"`),
    duo("(define (caddr l) (car (cdr (cdr l))))", "()"),
    duo(`(caddr ("a" "b" "c"))`, `"c"`),

    // ------------ alternation -------------
    duo(`(if (== 4 4) "ok" "nok")`, `"ok"`),
    duo(`(if (!= 4 4) "ok" "nok")`, `"nok"`),
    duo(`(define (codeSize l)
         (cond
           ((>= l 9) "L")
           ((>= l 5) "M")
           (else "S")
         ))`, `()`),
    duo(`(codeSize 10)`, `"L"`),
    duo(`(codeSize 6)`, `"M"`),
    duo(`(codeSize 2)`, `"S"`),

    // ------------------------
    duo(`(define (divisible? n m) (== (% n m) 0))`, `()`),
    duo(`(divisible? 1000000 10)`, TRUE),
    duo(`(divisible? 1000000 7)`, FALSE),
    duo(`(define (fact n) (if (<= n 0) 1 (* n (fact (- n 1)))))`, `()`),
    duo(`(fact 0)`, `1.000000`),
    duo(`(fact 4)`, `24.000000`),
    duo(`(fact 5)`, `120.000000`),
}

func Test(t *testing.T) {
    gm := grammar()
    m := NewMachine()
    // simply compare m.Eval(gm.ParseString(`k`)) with `v`.
    for _, o := range tests {
        k := o.expr
        v := o.expectation
        t.Run(fmt.Sprintf("eval %s expected to give %s", k, v), func(t *testing.T) {
            if ast, e := gm.ParseString(k); e == nil {
                s := stripansi(m.Eval(ast.(Expr)).ContentString())
                if s != v {
                    t.Errorf("%s expected to eval as %s gave %s instead\n", k, v, s)
                }
            } else {
                t.Errorf("%s expect to eval as %s did not even parse! error = %s\n", k, v, e)
            }
        })
    }
}

Grammar is very small, as in lisp everything is list:

import (
    j "github.com/grepsuzette/joeson"
)

// -- The parsing grammar

func i(a ...any) j.ILine                                 { return j.I(a...) }
func o(a ...any) j.OLine                                 { return j.O(a...) }
func named(name string, lineStringOrAst any) j.NamedRule { return j.Named(name, lineStringOrAst) }
func rules(a ...j.Line) []j.Line                         { return a }

var grammarRules = rules(
    o(named("toplevelexpr", "_ expr:expr _"), parseTopLevelExpr),
    i(named("expr", "l:list | s:string | n:number | operator:operator"), parseExpr),
    i(named("list", "'(' _ (expr*__) _ ')'"), parseList),
    i(named("operator", "word | '+' | '-' | '*' | '/' | '%' | '>=' | '<=' | '!=' | '=='| '<' | '>' |  '=' "), parseOperator),
    i(named("_", "(' ' | '\t' | '\n')*")),
    i(named("__", "(' ' | '\t' | '\n')+")),
    i(named("string", "'\"' s:([^\"]*) '\"'"), parseString),
    i(named("word", "/[a-zA-Z\\._][a-zA-Z\\._0-9?]*/")),
    i(named("number", "/-?[0-9]+/") /* TODO non-int */, func(it j.Ast) j.Ast { return j.NewNativeIntFrom(it) }),
    i(named(".", "/[\\s\\S]/")),
    i(named("ESC1", "'\\\\' .")),
)

This was a week-end experimentation that took a couple of days more. Lisp grammar is too simple for demonstration purposes, so I think I may make another example one day. Hopefully soon, but if I have proof somebody reads this it will accelerate things :p

Linking to #33

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant