view 2-Io/day3-DSL.io @ 46:eac30c1b92da

Add more notes and try to understand the operators
author IBBoard <dev@ibboard.co.uk>
date Wed, 20 Sep 2017 20:48:37 +0100
parents 8a2451a7b86e
children 409249712590
line wrap: on
line source

# Creating a simple "domain-specific language"
# (Except it's not really - it's just a little extension to support
# the "{ key:value[, key:value[…]] }" format of specifying dictionaries)

# Because this is a string then we can do it at any time!
OperatorTable addAssignOperator(":", "atPutThing")

# curlyBrackets is an automatically invoked function from the parser whenever
# it encounters curly brackets.
# This is hidden black magic that you need to know about to override it.
# It appears to call the method for all of the content within the brackets.
curlyBrackets := method(
    # Create a map
    map := Map clone
    # Foreach arg that got passed (each comma-separated line within the brackets)
    # then run that on the map
    # Note: It isn't clear why ":" operates on the map at this point
    # The book says "we'll execute `map atPutThing(a, b)`" but why?
    # Colon was originally on a string, not the map, so how does it know to invoke on the map?
    call message arguments foreach(arg,
        # Based on the following println then it is already "atPutThing(a, b)" by this point
        # but the ordering still doesn't make sense.
        # See also http://iolanguage.org/guide/guide.html#Syntax-Operators
        #
        # It *might* be a side-effect of how addAssignOperator works and then the map hijacks
        # the values, but that isn't clear from the book OR the documentation!
        #arg println
        map doMessage(arg)
    )
    # Return the map (last line's return is the return)
    map
)

Map atPutThing := method(
    # Call the standard method
    self atPut(
        # And pull the call args to pass through
        # NOTE: arg 1 has its quotes stripped automatically because it's a number
        # or some other magic!
        call evalArgAt(0) asMutable removePrefix("\"") removeSuffix("\""),
        call evalArgAt(1)
    )
)

# Note: Trailing commas break things in COMPLETELY CRYPTIC WAYS
# This is because we use doString, which executes it as a program,
# BUT the stack trace doesn't make this clear, so you end up chasing
# bugs in ENTIRELY THE WRONG PART OF THE CODE.
# Eval considered harmful.
the_string := "{
    \"foo\": \"12345\",
    \"bar\": \"7890\"
}"

phoneNumbers := doString(the_string)
phoneNumbers keys println
phoneNumbers values println

# It feels like implementing something like this should work,
# but it doesn't because "Number does not respond to ':'"
# because we assigned ":" as an assignment operator (like :=)
# and not as a normal operator, although it's then EVEN LESS CLEAR
# how the code in curlyBrackets works
# Also, Io Language docs are poor and don't make it clear how to:
# a) define a range that works; or
# b) do ANYTHING with the OperatorTable
Number atPutThing := method(
    call println
    return Range (call target) to(call evalArgAt(1))
)

# But presumably this will break, because there's no "atPutThing" method
a := 1
b := 5
range := a : b
# Yep - "Exception: Number does not respond to ':'"
# So you've got to be careful for reusable operators