Skip to content

Commit

Permalink
Strict AST validity checking, at end of compile
Browse files Browse the repository at this point in the history
AST errors were previously only being checked when a list was compiled
to a macro.  This meant that a whole lot of otherwise illegal cases were
not caught.  An error wouldn't be thrown either, allowing the erroneous
result to pass to stdout.

This was handy when everything was unstable, because errors would be
everywhere.  But with stuff having stabilised more recently, it starts
to make sense to check properly.

The test changes are necessary to pass the more rigorous checks.  Most
of them are pedantic stuff that aren't really relevant to the thing
being tested, like return statements having to be inside functions
always, and such.
  • Loading branch information
anko committed Sep 25, 2015
1 parent 3beaafd commit 31efaaf
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 42 deletions.
2 changes: 1 addition & 1 deletion doc/basics-reference.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ header, the second to be the right, and the rest to be body statements.
<!-- !test in for-in loop -->

(forin (var x) xs
((. console.log) (get xs x)))
((. console log) (get xs x)))

<!-- !test out for-in loop -->

Expand Down
17 changes: 9 additions & 8 deletions readme.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -183,18 +183,19 @@ Conditionals are built with the `if` macro:
<!-- !test in special form -->

; The "if" macro compiles to an if-statement
(if ok ; argument 1 becomes the conditional
(block (var x (! ok)) ; argument 2 the consequent
(return x))
(return false)) ; argument 3 (optional) the alternate
(if lunchtime ; argument 1 becomes the conditional
(block
(var lunch (find food)) ; argument 2 the consequent
(lunch))
(writeMoreCode)) ; argument 3 (optional) the alternate

<!-- !test out special form -->

if (ok) {
var x = !ok;
return x;
if (lunchtime) {
var lunch = find(food);
lunch();
} else
return false;
writeMoreCode();

Note how the block statement (`(block ...)`) has to be made explicit. Because
it's so common, other macros that accept a block statement as their last
Expand Down
16 changes: 1 addition & 15 deletions src/ast.ls
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
ast-errors = require \./esvalid-partial

looks-like-number = (atom-text) ->
atom-text.match /^\d+(\.\d+)?$/
looks-like-negative-number = (atom-text) ->
Expand Down Expand Up @@ -96,19 +94,7 @@ class list
if head instanceof atom
and local-env.find-macro head.text!

r = that.apply null, ([ local-env ] ++ rest)

check-for-ast-errors = ->
if ast-errors it
that.for-each -> console.error it
#throw Error "Invalid AST"

if typeof! r is \Array
r.for-each check-for-ast-errors
else
check-for-ast-errors r

r
that.apply null, ([ local-env ] ++ rest)

else
# TODO compile-time check if callee has sensible type
Expand Down
22 changes: 17 additions & 5 deletions src/translate.ls
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ environment = require \./env

{ create-transform-macro } = require \./import-macro

{ errors } = require \esvalid

module.exports = (ast, options) ->

transform-macros = (options.transform-macros || []) .map (func) ->
Expand All @@ -23,8 +25,18 @@ module.exports = (ast, options) ->
transform-macros .for-each (macro) ->
statements := macro.apply null, statements

type : \Program
body : statements
|> concat-map (.compile root-env)
|> (.filter (isnt null)) # because macro definitions emit null
|> (.map statementify)
program-ast =
type : \Program
body : statements
|> concat-map (.compile root-env)
|> (.filter (isnt null)) # because macro definitions emit null
|> (.map statementify)

err = errors program-ast
if err.length
first-error = err.0
console.error "[Error] #{first-error.message}\n\
Node: #{JSON.stringify first-error.node}"
throw first-error
else
return program-ast
26 changes: 13 additions & 13 deletions test.ls
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ test "plain literal" ->
esl "3"
..`@equals` "3;"

test "plain somewhat number-looking variable" ->
esl "3asd5"
..`@equals` "3asd5;"
test "plain literal with trailing digits" ->
esl "asd39"
..`@equals` "asd39;"

test "plain string literal" ->
esl '"ok then"'
Expand Down Expand Up @@ -189,16 +189,16 @@ test "empty statement" ->
..`@equals` ""

test "break and continue statements" ->
esl "(break) (continue)"
..`@equals` "break;\ncontinue;"
esl "(while true (break) (continue))"
..`@equals` "while (true) {\n break;\n continue;\n}"

test "break to label" ->
esl "(break label)"
..`@equals` "break label;"
esl "(label x (while true (break x)))"
..`@equals` "x:\n while (true) {\n break x;\n }"

test "continue to label" ->
esl "(continue label)"
..`@equals` "continue label;"
esl "(label x (while true (continue x)))"
..`@equals` "x:\n while (true) {\n continue x;\n }"

test "stand-alone label" ->
esl "(label x)"
Expand All @@ -213,8 +213,8 @@ test "labeled expression" ->
..`@equals` "label:\n x * 4;"

test "return statement" ->
esl "(return \"hello there\")"
..`@equals` "return 'hello there';"
esl "(function () (return \"hello there\"))"
..`@equals` "(function () {\n return 'hello there';\n});"

test "member expression" ->
esl "(. console log)"
Expand All @@ -240,15 +240,15 @@ test "switch statement" ->
esl '''
(switch (y)
((== x 5) ((. console log) "hi") (break))
(default (return false)))
(default (yes)))
'''
..`@equals` """
switch (y()) {
case x == 5:
console.log('hi');
break;
default:
return false;
yes();
}
"""

Expand Down

0 comments on commit 31efaaf

Please sign in to comment.