Sugar and Syntax
So I spent the last week working on desugaring and making edits to the grammar of my language after last weeks meeting.
It was all fine.
A seemingly common shorthand in newer languages is "x..y" to generate a list between x and y ranges.
I thought it would be a reasonable thing to expand in ast-manipulation.
So I went back to the kludgy RPN grammar I made a while ago to attempt it (instead of
generating an array, it expands to a series of ints to be put on a stack).
Here is the added file:
#lang racket
#|
desugarer.rkt
Logan Davis
DESCRIPTION:
Expands a lit-expansion token to its correct range of
int tokens.
TO USE:
Import into another racket file and call parse-expanders
on an AST.
The returned value is the expanded ast.
11/12/16 | Racket 6.6 | MIT License
|#
(require brag/support)
(define desugared-ast "")
(define expand-ranges
(lambda (datum)
(define desugared-datum '())
(cond [(symbol? datum) (set! desugared-ast (string-append desugared-ast
(symbol->string datum)
" "))]
[(string? (second datum)) (set! desugared-ast (string-append
desugared-ast
"("
(symbol->string (first datum))
" \""
(second datum)
"\")"))]
[(equal? (first (second datum)) 'lit-expansion) (generate-range
(second (second datum)))]
[(list? datum) (and (set! desugared-ast (string-append desugared-ast "("))
(map expand-ranges datum)
(set! desugared-ast (string-append desugared-ast ")")))])))
(define generate-range
(lambda (range-statement)
(define range-bounds (map string->number (string-split range-statement "..")))
(define numbers-to-tokenize (range (first range-bounds)
(+ 1 (second range-bounds))))
(for ([item numbers-to-tokenize])
(set! desugared-ast
(string-append desugared-ast
"(arg (num " "\""
(number->string item)
"\"" "))")))))
(define parse-expanders
(lambda (datum)
(expand-ranges datum)
(read (open-input-string desugared-ast))))
With the sample ast:
(define t '(program
(expression
(arg (lit-expansion "1..14"))
(lparen "(")
(expression (arg (num "5")) (arg (num "7")) (arg (id "z")) (op "*"))
(rparen ")")
(op "+"))))
This is returned:
'(program
(expression
(arg (num "1"))
(arg (num "2"))
(arg (num "3"))
(arg (num "4"))
(arg (num "5"))
(arg (num "6"))
(arg (num "7"))
(arg (num "8"))
(arg (num "9"))
(arg (num "10"))
(arg (num "11"))
(arg (num "12"))
(arg (num "13"))
(arg (num "14"))
(lparen "(")
(expression (arg (num "5")) (arg (num "7")) (arg (id "z")) (op "*"))
(rparen ")")
(op "+")))
I attached the second draft of the plan-language as a zip. But here is the example language file:
#/
multi line comments example.
Overview:
- Strongly typed.
- All statements end with ";"
- By default all values are immutable.
- function declares require a type signature:
* type sig = a list of args and their types followed
by a "->" and then the return type.
* example: define sum_list([num_lst:Array[Int]] -> Int){ }
- single statements can be made in direct c by calling "raw_c" and
passing it a string of the needed C code.
- functions are invoked by ML/F# syntax:
* <function_name> <values seperated by spaces>
* example: sumList [1,2,3,4,5] ;
- "mutable" is a wrapper which will decode values in mutable memory.
- Functions return mutable data types by default so they can be stored in
dynamic memory.
/#
let list_of_pins : array[int] = [1,2,3,4,5] ; // stored in program mem (immutable)
let on : mutable int = 7 * 8 - 5 ; // a dynamically stored variable (mutable)
//still requires a setup fucntion: runs once
def setup none -> none {
pinMode OUTPUT list_of_pins ;
raw_c "Serial.begin(9600)" ;
}
//still requires a loop function: runs repeatedly
def loop none -> none {
while(on){
setPins ON list_of_pins;
delay 500 ; //in millis
setPins OFF list_of_pins;
delay 100 * 5 ;
turn_pattern 1 ON ;
delay 500 ;
5 + 7 * 9;
}
}
def turn_pattern pattern:int, mode:int -> none {
// Turns pins on/off from two different patterns.
let pattern1 : array[int] = [1,2,3,5];
let pattern2 : array[int] = [3,6];
if pattern == 1 {
setPins mode pattern1;
}
if pattern == 2 {
setPins mode pattern2;
}
}
Yup.