I've got
(define (compiler exp)
(define (printer line) (display line) (newline))
(init-generators)
(let ((res (compile (append bootstrap (list exp)) function-res '())))
(map printer c-string-list)
(display bootstrap-c-code)
(add-c-function 'startup '(env) res)
(map (lambda (function) (map printer function) (newline)) c-function-list)
'ok))
and I need
(define (compile file-name)
(compiler (load file-name)))
But it doesn't work this way. It's executing directly. How can I load it as expression? (exp)
Are you looking for the read function, as opposed to load? Without knowing more about the implementation you're using, I can only point to the read documentation from r5rs. Use open-input-file to get a port from the filename, and then read from that port.
What's the content of the file file-name? If it's an expression, load will execute it, for instance if file-name contains (+ 1 1), (load filename) will return 2.
On the other hand, if file-name contains a list of symbols, they will be read as they are, for example if file-name contains '(+ 1 1) (notice the quote at the beginning), then (load filename) will return (+ 1 1), which I'm guessing is what you mean by saying that you need to load it as an expression.
If you need to load several expressions inside the file, surround them with a quote and a begin in the file:
'(begin
<your code here>
)
Related
I want to make a program that makes a conversion simulating a for loop, for example:
orig value converted
1 2.2
2 4.4
...
199 437.8
So far, what I have done is the following:
(define (conv ini)
(if (> ini 199)
0
(begin(
(display (* ini 2.2))
(newline)
(conv (+ ini 1))))))
but when I want to run it I got the following error:
arity mismatch;
the expected number of arguments does not match the given number
expected: 1
given: 2
arguments...:
I see that my recursion call is fine, so I cannot get it which one is the problem.
The syntax of begin is (begin form ...) not (begin (form ...)): your function should be
(define (conv ini)
(if (> ini 199)
0
(begin
(display (* ini 2.2))
(newline)
(conv (+ ini 1)))))
(Or, if you are enamoured of a syntax like that, you could define
(define-syntax beguine
(syntax-rules ()
[(_ (form ...))
(begin form ...)]))
and then (beguine (...)) will work.)
...
(begin(
(display (* ini 2.2))
...))))
In your code you actually try to execute (OP ...), where OP is the value of (display ...), which for sure is not a function.
So I doubt a lot your output is the output of what you pasted in your question.
For some code I was working I've needed to handle 'x inside macro. What is standard way of handling those values?
I have code like this:
(define (quoted-symbol? x)
(and (pair? x) (eq? (car x) 'quote) (symbol? (cadr x)) (null? (cddr x))))
(define-macro (test x)
(if (quoted-symbol? x)
`(begin
(display ',(cadr x))
(newline))))
(test 'hello) ;; 'hello will be expanded into list (quote hello)
Is this how this should be handled, or is just in macro you don't use quoted symbols?
NOTE: I'm not asking about hygienic macros (I'm asking about real lisp macros), so please no answers with hygienic macros.
EDIT:
My macro works correctly in Guile and BiwaScheme and in my own scheme like lisp in JavaScript. Here is better example:
(define-macro (test x)
(if (quoted-symbol? x)
`',(cadr x)))
(define (example arg)
(list arg (test 'world)))
(example 'hello)
the question was not about display, but about (cadr x).
EDIT2: You've asked so here you go, my macro:
(define-macro (--> expr . code)
"Helper macro that simplify calling methods on objects. It work with chaining
usage: (--> ($ \"body\")
(css \"color\" \"red\")
(on \"click\" (lambda () (print \"click\"))))
(--> document (querySelectorAll \"div\"))
(--> (fetch \"https://jcubic.pl\") (text) (match /<title>([^<]+)<\/title>/) 1)
(--> document (querySelectorAll \".cmd-prompt\") 0 \"innerText\")"
(let ((obj (gensym)))
`(let* ((,obj ,(if (and (symbol? expr) (not (null? (match /\./ (symbol->string expr)))))
`(.. ,expr)
`,expr)))
,#(map (lambda (code)
(let ((name (gensym))
(value (gensym)))
`(let* ((,name ,(cond ((quoted-symbol? code) (symbol->string (cadr code)))
((pair? code) (symbol->string (car code)))
(true code)))
(,value (. ,obj ,name)))
,(if (and (pair? code) (not (quoted-symbol? code)))
`(set! ,obj (,value ,#(cdr code)))
`(set! ,obj ,value)))))
code)
,obj)))
;; ---------------------------------------------------------------------------------------
(define (quoted-symbol? x)
"(quoted-symbol? code)
Helper function that test if value is quoted symbol. To be used in macros
that pass literal code that is transformed by parser.
usage:
(define-macro (test x)
(if (quoted-symbol? x)
`',(cadr x)))
(list 'hello (test 'world))"
(and (pair? x) (eq? (car x) 'quote) (symbol? (cadr x)) (null? (cddr x))))
the macro is used in my scheme like lisp in JavaScript, like the doc string suggest:
(--> document (querySelectorAll ".class") 0 "innerText")
I want to support:
(--> document (querySelectorAll ".class") 0 'innerText)
The code can be tested online at: https://jcubic.github.io/lips/ (You need to copy/paste the code since current version allow only method calls).
To get expansion you can use
(pprint (macroexpand (--> document (querySelector "x"))))
if it don't work (don't expand) it mean that macro is broken somehow.
dot is build in function that get property of an object and .. macro:
(define-macro (.. expr)
"(.. foo.bar.baz)
Macro that gets value from nested object where argument is comma separated symbol"
(if (not (symbol? expr))
expr
(let ((parts (split "." (symbol->string expr))))
(if (single parts)
expr
`(. ,(string->symbol (car parts)) ,#(cdr parts))))))
that can be use to get nested property like (.. document.body.innerHTML)
Scheme doesn't have "real lisp macros". Some implementations has something similar, but the forms have different names and uses. They are not portable at all.
The standard way of handling 'x is to handle it like an expression that gets evaluated in the expansion. Eg.
(define var 'x)
(test 'x)
(test var)
The two test forms should amount to the same even though the macro test gets (quote x) in the first and the symbol var in the second. At the time of the expansion var does not exist since the implementation can expand all the macros before starting.
You implementation of test will not work. Eg. the display might be run one or twice and then each time you call a procedure that uses it it will gfail since the expansion is the undefined value and it might not be fit for evaluation. eg.
(define (example arg)
(list arg (test 'w)))
When this is defined you get 'w or (quote w) printed with a newline and then the procedure it tries to store is:
(define (example arg)
(list arg #<undefined>))
Note that what constitutes the undefined value is chosen by the implementaion, but I know for sure that in many implementaions you cannot evaluate #<undefined>.
I am trying to write a scheme program that counts the number of if-statement a file containing code. I know how to read in the file but I don't know how to go about counting the number of if-statements.
This is very hard without actually implementing reducing the language to a more primitive form. As an example, imagine this:
(count-ifs '(let ((if +))
(if 1 2 3)))
; ==> 0
0 is the correct amount as if is a binding shadowing if and Scheme supports shadowing so the result of that expression is 6 and not 2. let can be rewritten such that you can check this instead:
(count-ifs '((lambda (if)
(if 1 2 3))
+))
; ==> 0
It might not look like an improvement, but here you can actually fix it:
(define (count-ifs expr)
(let helper ((expr expr) (count 0))
(if (or (not (list? expr))
(and (eq? (car expr) 'lambda)
(memq 'if (cadr expr))))
count
(foldl helper
(if (eq? (car expr) 'if)
(add1 count)
count)
expr))))
(count-ifs '((lambda (if)
(if 1 2 3))
(if #t + (if if if))))
; ==> 2
Challenge is to expand the macros. You actually need to make a macro expander to rewrite the code such that the only form making bindings would be lambda. This is the same amount of work as making 80% of a Scheme compiler since once you've dumbed it down the rest is easy.
A simple way to do it could be recursion structure like this:
(define (count-ifs exp)
(+ (if-expression? exp 1 0)))
(if (pair? exp)
(+ (count-ifs (car exp)) (count-ifs (cdr exp))))
0)))
But this might overcount.
A more correct way to do it would be to process the code by checking each type of expression you see - and when you enter a lambda you need to add the variables it binds to a shadowed symbols list.
If Guile is not the best Scheme for this usage, then which one should I be looking at? I'm basically looking for a Guile equivalent of awk '{print $N}'. If Scheme can't do this, then I'd like to know why not.
Guile changed its I/O a bit between 2.0 and 2.2, so this uses r6rs I/O which (hopefully) works the same in both, but I haven't tested with 2.2.
This can be optimized further.
#!/usr/bin/guile \
-e start -s
!#
(use-modules (rnrs io ports))
;;; Reads one line from current-input-port and prints the field indicated by
;;; field-num. If the line does not have enough fields, it prints a newline.
;;; Returns the field, an empty string, or #f if end of file is reached.
(define (get-field field-num)
(let ((line (get-line (current-input-port))))
(if (eof-object? line)
#f
(let ((fields (string-tokenize line)))
(if (< field-num (length fields))
(let ((field (list-ref fields field-num)))
(put-string (current-output-port)
(string-append field "\n"))
field)
(and (put-string (current-output-port) "\n")
""))))))
;;; Repeat get-field until until end of file is reached
(define (get-all-fields field-num)
(if (get-field field-num)
(get-all-fields field-num)
#f))
(define (start args)
(if (and (> (length args) 1)
(integer? (string->number (list-ref args 1))))
(get-all-fields (1- (string->number (list-ref args 1))))
(display (string-join
`("Usage:" ,(list-ref args 0) "<field-number>\n")
" "))))
At my blog I have an essay giving a set of functions that make it easy to handle delimited text files.
What's the best way to read input from stdin in racket?
In particular I'd like something like cin from c++ or scanf from c where I specify the types of things I want read and they are returned.
read-line is easy. To be portable across Unix and Windows, additional option is required.
(read-line (current-input-port) 'any)
Return and linefeed characters are detected after the conversions that
are automatically performed when reading a file in text mode. For
example, reading a file in text mode on Windows automatically changes
return-linefeed combinations to a linefeed. Thus, when a file is
opened in text mode, 'linefeed is usually the appropriate read-line
mode.
So, 'any is required to be portable when the input port is not a file (standard input).
Test program:
#lang racket
(let loop ()
(display "Input: ")
(define a (read-line (current-input-port) 'any))
(printf "input: ~a, length: ~a, last character: ~a\n"
a
(string-length a)
(char->integer (string-ref a (- (string-length a) 1))))
(loop))
In Windows, replace (read-line (current-input-port) 'any) with (read-line) and see what happens.
You can do pretty much everything you want to... at the low level, I would suggest (read-line) and (read-bytes). For higher-level processing (as e.g. scanf does), I would suggest a regexp-match on the input. For instance
(regexp-match #px" *([0-9]+)" (current-input-port))
I'd use the read procedure for the general case. If the data type to be read is known beforehand, use read-char, read-string, read-bytes.
Also, take a look at this implemenation for reading formatted input - a scanf in Scheme.
Here's a basis for line-by-line processing in Racket Scheme. It doesn't split an input line into multiple words, or do typed input, but this seems like a good place to put it.
(define (get)
(read-line (current-input-port)))
(define (put . vs)
(for-each display vs)
(displayln ""))
(define (sed fn)
(let ((line (get)))
(if (not (eof-object? line))
(begin
(fn line)
(sed fn))
'true)))
(sed (lambda (line)
(put "Hello, " line)))
Here's one that does split input, also encodes CSV for good measure.
(define (get)
(read-line (current-input-port)))
(define split string-split)
(define sep ",")
(define enc identity)
(define (enc-csv s)
(string-append "\"" (string-replace s "\"" "\"\"") "\""))
(define enc enc-csv)
(define (put . vs)
(displayln (string-join (map enc vs) sep)))
(define (sed fn)
(let ((line (get)))
(if (not (eof-object? line))
(begin
(fn line)
(sed fn))
'true)))
(sed (lambda (line)
(apply put (split line))))
This works in Racket. I'm not sure how much of it is specific to Racket. It doesn't seem to work in Chicken or Guile.