forth Sample
Blind Watch Maker 1
Download
Find Words
forth
Fractal Benchmark
Genetic Algorithm
Gtk Source Editor
Hex Dump
Notepad
Point
Shapes
Simple English Parser
Sizes
TPK
Word Count
"""
forth.cobra

This is a budding FORTH interpreter featuring:
* basic stack item manipulation (dup, over, swap, drop, ...)
* stack manipulation (0SP aka stack.clear)
* math (+ - * / %)
* definitions (; NAME CODE :  -OR-  def NAME CODE end)
* You can multiply strings as in: foo 3 * -- foofoofoo

...and not much else right now.

Run with no arguments to see some example FORTH get executed.

Run with -i for "interactive mode" to enter your own FORTH.
Enter "quit" or "exit" to do so.

Forth tutorial:
http://www.softsynth.com/pforth/pf_tut.htm

In addition to some extra synonym words which you'll see in the list of words, this particular
interperter also accepts 'def' for ':' and 'end' for ';'. So these are equivalent:

    : AVERAGE ( a b -- avg ) + 2 / ;
    def AVERAGE ( a b -- avg ) + 2 / end

TODO:
    [ ] Text in parens is a comment.
    [ ] variables
    [ ] Could do the built-in definitions by tagging their methods with attributes
    [ ] Support fractional numbers. In other words, decimals.
    [ ] Automated tests.
    [ ] Consider making Definition abstract with concrete subs: BuiltInDefinition, WordsDefinition with possible DelegateDefinition as well
    [ ] Could preserve the comment in a definition that immediatel follows the definition's name and store it with the definition. Then could offer it back later with a help command.
    [ ] Shouldn't there be some kind of quoting character so a name can be pushed on the stack without invoking it? Probably have not gotten that far in the tutorial.
    [ ] More FORTH:
- In general, cover everything at http://www.softsynth.com/pforth/pf_tut.htm
ROT ( a b c -- b c a , ROTate third item to top ) 
PICK ( ... v3 v2 v1 v0 N -- ... v3 v2 v1 v0 vN ) 
      0 PICK is equivalent to DUP
      1 PICK is equivalent to OVER
DROP ( n -- , remove top of stack ) 
?DUP ( n -- n n | 0 , duplicate only if non-zero, '|' means OR ) 
-ROT ( a b c -- c a b , rotate top to third position ) 
2SWAP ( a b c d -- c d a b , swap pairs ) 
2OVER ( a b c d -- a b c d a b , leapfrog pair ) 
2DUP ( a b -- a b a b , duplicate pair ) 
2DROP ( a b -- , remove pair ) 
NIP ( a b -- b , remove second item from stack ) 
TUCK ( a b -- b a b , copy top item to third position )  

Fast math operations:
1+ 1- 2+ 2- 2* 2/

Define new words:
      : AVERAGE ( a b -- avg ) + 2/ ;
"""


class ForthMachine

    var _stack as Stack<of Word>
    var _definitions as Dictionary<of Word, Definition>
    var _didQuit as bool
    var _verbosity = 1
    
    cue init
        base.init
        _stack = Stack<of Word>()
        _definitions = Dictionary<of Word, Definition>()

        .define('quit', 'built-in:quit')
        .define('exit', 'built-in:quit')

        .define('words', 'built-in:printDefinitionNames')

        .define('.', 'built-in:printTop')
        .define('.S', 'built-in:printStack')
        .define('stack.print', 'built-in:printStack')

        .define('0SP', 'built-in:clearStack')
        .define('stack.clear', 'built-in:clearStack')

        .define('drop', 'built-in:drop')
        .define('dup', 'built-in:dup')
        .define('over', 'built-in:over')
        .define('swap', 'built-in:swap')

        .define('+', 'built-in:add')
        .define('-', 'built-in:sub')
        .define('*', 'built-in:mul')
        .define('/', 'built-in:div')
        .define('%', 'built-in:mod')

    get didQuit from var

    pro verbosity from var

    ## Fundamental operations to implement a FORTH machine

    def wordFor(text as String) as Word
        require text.trim.length
        return Word(text)

    def wordFor(i as int) as Word
        return Word(i)

    def push(word as Word)
        require word.isString implies word.string.trim.length
        _stack.push(word)

    def push(word as String)
        .push(Word(word))

    def peek as Word
        if _stack.count
            return _stack.peek
        else
            print 'ERROR: Stack is empty.'
            return Word(0)

    def pop as Word
        if _stack.count
            return _stack.pop
        else
            print 'ERROR: Stack is empty.'
            return Word(0)

    def define(word as String, definition as String)
        words = for rawWord in definition.split where rawWord.trim.length>0 get .wordFor(rawWord.trim)  # CC: axe > 0
        _definitions[.wordFor(word)] = Definition(words)

    def define(word as Word, definition as IEnumerable<of Word>)
        _definitions[word] = Definition(definition)

    def definitionFor(word as Word) as Definition?
        result as Definition?
        if _definitions.tryGetValue(word, out result)
            return result to !
        else
            return nil

    def invoke(name as Word, definition as Definition)
        if definition.count
            word = definition[0]
            if word.toString.startsWith('built-in:')
                methodName = word.toString['built-in:'.length:]
                methodName = methodName[0].toString.toUpper + methodName[1:] # because at run-time, method names are capped for compatibility with C# and VB
                method = .typeOf.getMethod(methodName)
                method.invoke(this, nil)
            else
                for word in definition
                    if word.isString
                        defin = .definitionFor(word)
                        if defin
                            .invoke(word, defin)
                            continue
                    .push(word)
        else
            # empty definition is a no-op
            pass

    def error(message as String)
        print 'ERROR:', message


    ## Conveniences

    def processInput(input as String)
        mode = 0  # 0 - normal, 1 - define
        if _verbosity
            print 'input>', input
        rawWords = input.trim.split
        for rawWord in rawWords
            rawWord = rawWord.trim
            if not rawWord.length
                continue
            branch mode
                on 0  # normal
                    if rawWord in [':', 'def']
                        defWords = List<of Word>()
                        mode = 1  # into define mode
                    else
                        word = .wordFor(rawWord)
                        if word.isString
                            defin = .definitionFor(word)
                            if defin
                                .invoke(word, defin)
                                continue
                        .push(word)
                on 1  # definition mode
                    if rawWord in [';', 'end']
                        .define(defWords[0], defWords[1:])
                        defWords.clear
                        mode = 0  # back to normal
                    else
                        defWords.add(.wordFor(rawWord))
        if _verbosity
            print 'stack> ' stop
            .printStack
            print


    ## Built-in definitions

    def quit
        _didQuit = true

    def printDefinitionNames
        keys = List<of Word>(_definitions.keys)
        keys.sort
        for key in keys, print '[key] ' stop
        print

    def printTop
        print .pop

    def printStack
        for word in Stack<of Word>(_stack)  # Stack wrapper reverses the order so right most printed element is the top of the stack
            print '[word] | ' stop
        print


    ## Built-ins: whole stack manipulation

    def clearStack
        _stack.clear


    ## Built-ins: stack element manipulations
    
    def drop
        .pop

    def dup
        .push(.peek)

    def over
        """
        a b -- a b a
        """
        b = .pop
        a = .pop
        .push(a)
        .push(b)
        .push(a)

    def swap
        """
        a b -- b a
        """
        b = .pop
        a = .pop
        .push(b)
        .push(a)


    ## Built-ins: arithmetic
    
    def add
        b = .pop
        a = .pop
        if a.isInt and b.isInt
            .push(.wordFor(a.int + b.int))
        else if a.isString and b.isString
            .push(.wordFor(a.string + b.string))
        else
            .error('Cannot add "[a]" and "[b]".')

    def sub
        b = .pop
        a = .pop
        if a.isInt and b.isInt
            .push(.wordFor(a.int - b.int))
        else
            .error('Cannot subtract "[a]" and "[b]".')

    def mul
        b = .pop
        a = .pop
        if a.isInt and b.isInt
            .push(.wordFor(a.int * b.int))
            return
        else if a.isString and b.isInt
            s = a.string
            i = b.int
        else if a.isInt and b.isString
            s = b.string
            i = a.int
        else
            .error('Cannot add "[a]" and "[b]".')
        sb = StringBuilder()
        for j in i, sb.append(s)
        .push(.wordFor(sb.toString))

    def div
        b = .pop
        a = .pop
        if a.isInt and b.isInt
            .push(.wordFor(a.int // b.int))
        else
            .error('Cannot divide "[a]" and "[b]".')

    def mod
        b = .pop
        a = .pop
        if a.isInt and b.isInt
            .push(.wordFor(a.int % b.int))
        else
            .error('Cannot modulate "[a]" and "[b]".')


class Word implements IComparable<of Word>

    enum WordType
        String
        Int

    var _which as WordType
    var _i as int
    var _s as String?

    cue init(text as String)
        """
        Creates a new word, interpreting the text as an integer if possible.
        """
        .init(text, true)

    cue init(text as String, interpret as bool)
        """
        When interpretation is false, text is simply taken as is.
        Otherwise if the text represents an integer, this word will be one.
        """
        test
            w = Word('foo')
            assert w.isString
            w = Word('10')
            assert w.isInt
            w = Word('10', false)
            assert w.isString
        body
            base.init
            if interpret and int.tryParse(text, out _i)
                _which = WordType.Int
            else
                _s = text
                _which = WordType.String

    cue init(i as int)
        base.init
        _i = i
        _which = WordType.Int

    get isInt as bool
        return _which == WordType.Int

    get int as int
        require .isInt
        return _i

    get isString as bool
        return _which == WordType.String

    get string as String
        require .isString
        return _s to !

    def equals(other as Object?) as bool is override
        if other inherits Word
            if _which == other._which
                branch _which
                    on WordType.String, return _s == other._s
                    on WordType.Int, return _i == other._i
            return false
        else
            return false
    
    def compareTo(other as Word) as int
        if .isInt
            if other.isInt
                return .int.compareTo(other.int)
            else
                return -1
        else if .isString
            if other.isString
                return .string.compareTo(other.string)
            else
                return 1
        else
            throw FallThroughException(other)

    def getHashCode as int is override
        branch _which
            on WordType.String, return _s.getHashCode
            on WordType.Int, return _i.getHashCode
        throw FallThroughException(_which)

    def toString as String is override
        branch _which
            on WordType.String
                if _s == '', return '(EMPTY-STRING)'
                if _s.trim == '', return '(BLANK-STRING)'
                return _s to !
            on WordType.Int
                return _i.toString
        return ''

    def toTechString as String
        branch _which
            on WordType.String, return '[.typeOf.name](string, "[_s]")'
            on WordType.Int, return '[.typeOf.name](int, "[_i]")'
        throw FallThroughException(_which)


class Definition inherits List<of Word>

    cue init(words as IEnumerable<of Word>?)
        base.init(words)


class Program

    shared

        def main
            print 'FORTH interpeter written in Cobra [CobraCore.version]'
            args = CobraCore.commandLineArgs
            if args.count > 1 and args[1] == '-i'
                .interactive
            else
                .runExamples
                print 'Run with -i for interactive mode.'

        def runExamples
            print 'Examples/tests:'
            print
            fm = ForthMachine()
            fm.printDefinitionNames
            fm.processInput('1 .')
            fm.processInput('1 2 +')
            fm.processInput('stack.clear')
            fm.processInput('2 dup + .')
            fm.processInput('1 3 swap')
            fm.processInput('drop drop')
            fm.processInput('3 5 over')
#            fm.processInput(': AVERAGE ( a b -- avg ) + 2 / ; words stack.clear 10 20 AVERAGE')
            fm.processInput(': AVERAGE + 2 / ; words stack.clear 10 20 AVERAGE .')
            fm.processInput('def double 2 * end  2  double dup .   double dup .  double dup .  drop')

        def interactive
            print 'Enter "quit" or "exit" to do so.'
            print
            fm = ForthMachine()
            fm.verbosity = 0
            print 'words:'
            fm.printDefinitionNames
            print
            while true
                print 'forth> ' stop
                input = Console.readLine
                if input
                    fm.processInput(input)
                    if fm.didQuit, break
                    print 'stack> ' stop
                    fm.printStack
                    print
                else
                    break