summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chapter01/src/main/java/com/seaofnodes/simple/parser/Ast.java127
-rw-r--r--chapter01/src/main/java/com/seaofnodes/simple/parser/Parser.java75
-rw-r--r--chapter01/src/test/java/com/seaofnodes/simple/lexer/TestParser.java20
-rw-r--r--common/src/main/java/com/seaofnodes/simple/common/CompilerException.java11
-rw-r--r--common/src/main/java/com/seaofnodes/simple/common/Severity.java19
-rw-r--r--common/src/main/java/com/seaofnodes/simple/lexer/aa/Lexer.java228
-rw-r--r--common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/CompilerException.java28
-rw-r--r--common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/ErrorListener.java (renamed from common/src/main/java/com/seaofnodes/simple/common/ErrorListener.java)21
-rw-r--r--common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Lexer.java15
-rw-r--r--common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Severity.java36
-rw-r--r--common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Source.java2
-rw-r--r--common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Token.java15
-rw-r--r--common/src/main/java/com/seaofnodes/util/Handy.java6
-rw-r--r--common/src/test/java/com/seaofnodes/simple/lexer/TestLexer.java2
14 files changed, 258 insertions, 347 deletions
diff --git a/chapter01/src/main/java/com/seaofnodes/simple/parser/Ast.java b/chapter01/src/main/java/com/seaofnodes/simple/parser/Ast.java
index a93b230f..b11d1ddb 100644
--- a/chapter01/src/main/java/com/seaofnodes/simple/parser/Ast.java
+++ b/chapter01/src/main/java/com/seaofnodes/simple/parser/Ast.java
@@ -1,48 +1,94 @@
+/*
+ * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ * <p>
+ * Contributor(s):
+ * <p>
+ * The Initial Developer of the Original Software is Dibyendu Majumdar.
+ * <p>
+ * This file is part of the Sea Of Nodes Simple Programming language
+ * implementation. See https://github.com/SeaOfNodes/Simple
+ * <p>
+ * The contents of this file are subject to the terms of the
+ * Apache License Version 2 (the "APL"). You may not use this
+ * file except in compliance with the License. A copy of the
+ * APL may be obtained from:
+ * http://www.apache.org/licenses/LICENSE-2.0
+ */
package com.seaofnodes.simple.parser;
-import com.seaofnodes.simple.common.CompilerException;
import com.seaofnodes.simple.lexer.ecstasy.Token;
import java.util.ArrayList;
import java.util.List;
+import java.util.stream.Collectors;
-public abstract class Ast {
+/**
+ * A simple AST definition.
+ */
+public class Ast {
- public static class Node {
+ private Ast() {
}
- public static class Expr extends Node {
+ public interface Node {
+ }
+
+ public abstract static class Expr implements Node {
public final Token op;
- public Expr(Token op) {
+ protected Expr(Token op) {
this.op = op;
}
}
+ /**
+ * A name in the language
+ */
public static class Identifier extends Expr {
public final String name;
+
public Identifier(Token op) {
super(op);
name = op.getValueText();
}
+
+ @Override
+ public String toString() {
+ return name;
+ }
}
+ /**
+ * Reference to a name in the language
+ */
public static class Symbol extends Expr {
public final String name;
+
public Symbol(Token op) {
super(op);
name = op.getValueText();
}
+
+ @Override
+ public String toString() {
+ return name;
+ }
}
public static class Binary extends Expr {
- public final Expr expr1, expr2;
+ public final Expr expr1;
+ public final Expr expr2;
public Binary(Token op, Expr expr1, Expr expr2) {
super(op);
this.expr1 = expr1;
this.expr2 = expr2;
}
+
+ @Override
+ public String toString() {
+ return "(" + expr1 + op.getValueText() + expr2 + ")";
+ }
}
public static class Unary extends Expr {
@@ -52,49 +98,65 @@ public abstract class Ast {
super(op);
this.expr = expr;
}
+
+ @Override
+ public String toString() {
+ return "(" + op.getValueText() + "(" + expr + "))";
+ }
}
public static class Constant extends Expr {
public Constant(Token op) {
super(op);
}
+
+ @Override
+ public String toString() {
+ return op.getValueText();
+ }
}
- public static class Statement extends Node {
+ public abstract static class Statement implements Node {
}
public static class IfElse extends Statement {
public final Expr expr;
- public final Statement ifStmt, elseStmt;
+ public final Statement ifStmt;
+ public final Statement elseStmt;
public IfElse(Expr expr, Statement ifStmt, Statement elseStmt) {
this.expr = expr;
this.ifStmt = ifStmt;
this.elseStmt = elseStmt;
}
+
+ @Override
+ public String toString() {
+ if (elseStmt != null)
+ return "if(" + expr + ") " + ifStmt + " else " + elseStmt;
+ return "if(" + expr + ") " + ifStmt;
+ }
}
public static class While extends Statement {
- public Expr expr;
- public Statement stmt;
+ public final Expr expr;
+ public final Statement stmt;
- public While() {
- }
- public void init(Expr expr, Statement stmt) {
+ public While(Expr expr, Statement stmt) {
this.expr = expr;
this.stmt = stmt;
}
+
+ @Override
+ public String toString() {
+ return "while(" + expr.toString() + ") " + stmt;
+ }
}
public static class Break extends Statement {
- public final Statement stmt;
-
- public Break(Statement stmt) {
- if (stmt == null)
- {
- throw new CompilerException("uneclosed break");
- }
- this.stmt = stmt;
+ @Override
+ public String toString() {
+ return "break;";
}
}
@@ -106,14 +168,24 @@ public abstract class Ast {
this.identifier = identifier;
this.expr = expr;
}
+
+ @Override
+ public String toString() {
+ return identifier.toString() + "=" + expr.toString() + ";";
+ }
}
- public static class Type extends Node {
+ public static class Type implements Node {
public final Token.Id typeId;
public Type(Token.Id typeId) {
this.typeId = typeId;
}
+
+ @Override
+ public String toString() {
+ return typeId.TEXT;
+ }
}
public static class Declare extends Statement {
@@ -124,9 +196,20 @@ public abstract class Ast {
this.type = type;
this.identifier = identifier;
}
+
+ @Override
+ public String toString() {
+ return type.toString() + " " + identifier.toString() + ";";
+ }
}
public static class Block extends Statement {
public final List<Statement> stmtList = new ArrayList<>();
+
+ @Override
+ public String toString() {
+ return "{" +
+ stmtList.stream().map(Object::toString).collect(Collectors.joining()) + "}";
+ }
}
}
diff --git a/chapter01/src/main/java/com/seaofnodes/simple/parser/Parser.java b/chapter01/src/main/java/com/seaofnodes/simple/parser/Parser.java
index b36942d4..263d0b41 100644
--- a/chapter01/src/main/java/com/seaofnodes/simple/parser/Parser.java
+++ b/chapter01/src/main/java/com/seaofnodes/simple/parser/Parser.java
@@ -1,4 +1,4 @@
-/**
+/*
* DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Contributor(s):
@@ -16,28 +16,28 @@
*/
package com.seaofnodes.simple.parser;
-import com.seaofnodes.simple.common.CompilerException;
+import com.seaofnodes.simple.lexer.ecstasy.CompilerException;
import com.seaofnodes.simple.lexer.ecstasy.Lexer;
import com.seaofnodes.simple.lexer.ecstasy.Token;
-import com.seaofnodes.simple.common.Severity;
+import com.seaofnodes.simple.lexer.ecstasy.Severity;
public class Parser {
final Lexer lexer;
Token look;
- Ast.Statement statementEnclosing;
public Parser(Lexer lexer) {
this.lexer = lexer;
}
- Token nextOrEof() {
+ private Token nextOrEof() {
if (lexer.hasNext())
return lexer.next();
return new Token(look.getStartPosition(), look.getEndPosition(), Token.Id.EOF);
}
- void nextToken() {
+ private void nextToken() {
look = nextOrEof();
+ // skip comments
while (look.isComment()) {
look = nextOrEof();
}
@@ -47,12 +47,12 @@ public class Parser {
}
}
- void error(Token t, String errorMessage) {
+ private void error(Token t, String errorMessage) {
lexer.log(Severity.ERROR, errorMessage, t.getStartPosition(), t.getEndPosition());
throw new CompilerException(errorMessage + ": " + t.toDebugString());
}
- void match(Token.Id tag) {
+ private void match(Token.Id tag) {
if (look.getId() == tag) {
nextToken();
} else {
@@ -65,43 +65,40 @@ public class Parser {
return parseProgram();
}
- Ast.Statement parseProgram() {
+ private Ast.Statement parseProgram() {
var block = new Ast.Block();
parseDeclarations(block);
parseStatements(block);
return block;
}
- void parseDeclarations(Ast.Block block) {
- while (look.getId() == Token.Id.VAR) {
+ private boolean isTypeName(Token tok) {
+ return switch (tok.getId()) {
+ case TYPE_INT, TYPE_CHAR, TYPE_FLOAT -> true;
+ default -> false;
+ };
+ }
+
+ private void parseDeclarations(Ast.Block block) {
+ while (isTypeName(look)) {
+ var type = new Ast.Type(look.getId());
nextToken();
var tok = look;
match(Token.Id.IDENTIFIER);
- match(Token.Id.COLON);
- var type = parseType();
match(Token.Id.SEMICOLON);
var identifier = new Ast.Identifier(tok);
block.stmtList.add(new Ast.Declare(type, identifier));
}
}
- Ast.Type parseType() {
- if (look.getId() == Token.Id.LIT_INTA) {
- nextToken();
- return new Ast.Type(look.getId());
- }
- error(look, "syntax error: unsupported type (at present only Int type is allowed)");
- return null;
- }
-
- void parseStatements(Ast.Block block) {
+ private void parseStatements(Ast.Block block) {
while (look.getId() != Token.Id.EOF &&
look.getId() != Token.Id.R_CURLY) {
block.stmtList.add(parseStatement());
}
}
- Ast.Statement parseStatement() {
+ private Ast.Statement parseStatement() {
Ast.Expr x;
Ast.Statement s1;
Ast.Statement s2;
@@ -120,22 +117,17 @@ public class Parser {
return new Ast.IfElse(x, s1, s2);
}
case WHILE -> {
- var whilenode = new Ast.While();
- var savedStmt = statementEnclosing;
- statementEnclosing = whilenode;
match(Token.Id.WHILE);
match(Token.Id.L_PAREN);
x = parseBool();
match(Token.Id.R_PAREN);
s1 = parseStatement();
- whilenode.init(x, s1);
- statementEnclosing = savedStmt;
- return whilenode;
+ return new Ast.While(x, s1);
}
case BREAK -> {
match(Token.Id.BREAK);
match(Token.Id.SEMICOLON);
- return new Ast.Break(statementEnclosing);
+ return new Ast.Break();
}
case L_CURLY -> {
return parseBlock();
@@ -146,7 +138,7 @@ public class Parser {
}
}
- Ast.Statement parseBlock() {
+ private Ast.Statement parseBlock() {
match(Token.Id.L_CURLY);
var block = new Ast.Block();
parseDeclarations(block);
@@ -155,16 +147,17 @@ public class Parser {
return block;
}
- Ast.Statement parseAssign() {
+ private Ast.Statement parseAssign() {
+ Token tok = look;
match(Token.Id.IDENTIFIER);
- Ast.Identifier identifier = new Ast.Identifier(look);
+ Ast.Identifier identifier = new Ast.Identifier(tok);
match(Token.Id.ASN);
var s = new Ast.Assign(identifier, parseBool());
match(Token.Id.SEMICOLON);
return s;
}
- Ast.Expr parseBool() {
+ private Ast.Expr parseBool() {
var x = parseAnd();
while (look.getId() == Token.Id.COND_OR) {
var tok = look;
@@ -174,7 +167,7 @@ public class Parser {
return x;
}
- Ast.Expr parseAnd() {
+ private Ast.Expr parseAnd() {
var x = parseRelational();
while (look.getId() == Token.Id.COND_AND) {
var tok = look;
@@ -184,7 +177,7 @@ public class Parser {
return x;
}
- Ast.Expr parseRelational() {
+ private Ast.Expr parseRelational() {
var x = parseAddition();
while (look.getId() == Token.Id.COMP_EQ ||
look.getId() == Token.Id.COMP_NEQ ||
@@ -199,7 +192,7 @@ public class Parser {
return x;
}
- Ast.Expr parseAddition() {
+ private Ast.Expr parseAddition() {
var x = parseMultiplication();
while (look.getId() == Token.Id.ADD ||
look.getId() == Token.Id.SUB) {
@@ -210,7 +203,7 @@ public class Parser {
return x;
}
- Ast.Expr parseMultiplication() {
+ private Ast.Expr parseMultiplication() {
var x = parseUnary();
while (look.getId() == Token.Id.MUL ||
look.getId() == Token.Id.DIV) {
@@ -221,7 +214,7 @@ public class Parser {
return x;
}
- Ast.Expr parseUnary() {
+ private Ast.Expr parseUnary() {
if (look.getId() == Token.Id.SUB ||
look.getId() == Token.Id.NOT) {
var tok = look;
@@ -232,7 +225,7 @@ public class Parser {
}
}
- Ast.Expr parsePrimary() {
+ private Ast.Expr parsePrimary() {
switch (look.getId()) {
case L_PAREN -> {
nextToken();
diff --git a/chapter01/src/test/java/com/seaofnodes/simple/lexer/TestParser.java b/chapter01/src/test/java/com/seaofnodes/simple/lexer/TestParser.java
index a89c1f6a..80917c0f 100644
--- a/chapter01/src/test/java/com/seaofnodes/simple/lexer/TestParser.java
+++ b/chapter01/src/test/java/com/seaofnodes/simple/lexer/TestParser.java
@@ -1,4 +1,4 @@
-/**
+/*
* DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Contributor(s):
@@ -15,10 +15,9 @@
package com.seaofnodes.simple.lexer;
-import com.seaofnodes.simple.common.ErrorListener;
+import com.seaofnodes.simple.lexer.ecstasy.ErrorListener;
import com.seaofnodes.simple.lexer.ecstasy.Lexer;
import com.seaofnodes.simple.lexer.ecstasy.Source;
-import com.seaofnodes.simple.parser.Ast;
import com.seaofnodes.simple.parser.Parser;
import org.junit.Assert;
import org.junit.Test;
@@ -28,30 +27,28 @@ public class TestParser {
@Test
public void testParseSingleDecl()
{
- final String source = "var i: Int;";
+ final String source = "int i;";
Lexer lexer = new Lexer(new Source(source), new ErrorListener());
Parser parser = new Parser(lexer);
var ast = parser.parse();
- Assert.assertTrue(ast instanceof Ast.Block
- && ((Ast.Block) ast).stmtList.size() == 1);
+ Assert.assertEquals("{int i;}", ast.toString());
}
@Test
public void testParseTwoDecls() {
- final String source = "var i: Int;\nvar j: Int;";
+ final String source = "int i;\nint j;";
Lexer lexer = new Lexer(new Source(source), new ErrorListener());
Parser parser = new Parser(lexer);
var ast = parser.parse();
- Assert.assertTrue(ast instanceof Ast.Block
- && ((Ast.Block) ast).stmtList.size() == 2);
+ Assert.assertEquals("{int i;int j;}", ast.toString());
}
@Test
public void testIfAndWhile() {
final String source =
"""
- var i: Int;
- var j: Int;
+ int i;
+ int j;
i = 0;
j = 100;
@@ -64,5 +61,6 @@ public class TestParser {
Lexer lexer = new Lexer(new Source(source), new ErrorListener());
Parser parser = new Parser(lexer);
var ast = parser.parse();
+ Assert.assertEquals("{int i;int j;i=0;j=100;while(1) {i=(i+1);j=(j-1);if((i>=j)) break;}}", ast.toString());
}
}
diff --git a/common/src/main/java/com/seaofnodes/simple/common/CompilerException.java b/common/src/main/java/com/seaofnodes/simple/common/CompilerException.java
deleted file mode 100644
index 518dcde6..00000000
--- a/common/src/main/java/com/seaofnodes/simple/common/CompilerException.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.seaofnodes.simple.common;
-
-public class CompilerException extends RuntimeException {
- public CompilerException(String message) {
- super(message);
- }
-
- public CompilerException(String message, Throwable cause) {
- super(message, cause);
- }
-}
diff --git a/common/src/main/java/com/seaofnodes/simple/common/Severity.java b/common/src/main/java/com/seaofnodes/simple/common/Severity.java
deleted file mode 100644
index f3325d52..00000000
--- a/common/src/main/java/com/seaofnodes/simple/common/Severity.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.seaofnodes.simple.common;
-
-
-/**
- * Severity levels.
- */
-public enum Severity
- {
- NONE, INFO, WARNING, ERROR, FATAL;
-
- public String desc()
- {
- String sName = name();
- StringBuilder sb = new StringBuilder(sName.length());
- sb.append(sName.charAt(0));
- sb.append(sName.substring(1).toLowerCase());
- return sb.toString();
- }
- }
diff --git a/common/src/main/java/com/seaofnodes/simple/lexer/aa/Lexer.java b/common/src/main/java/com/seaofnodes/simple/lexer/aa/Lexer.java
deleted file mode 100644
index 162cdea1..00000000
--- a/common/src/main/java/com/seaofnodes/simple/lexer/aa/Lexer.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/**
- * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Contributor(s):
- *
- * The Initial Developer of the Original Software is Cliff Click.
- *
- * This file is part of the Sea Of Nodes Simple Programming language
- * implementation. See https://github.com/SeaOfNodes/Simple
- *
- * The contents of this file are subject to the terms of the
- * Apache License Version 2 (the "APL"). You may not use this
- * file except in compliance with the License. A copy of the
- * APL may be obtained from:
- * http://www.apache.org/licenses/LICENSE-2.0
- */
-package com.seaofnodes.simple.lexer.aa;
-
-import com.seaofnodes.util.AryInt;
-import com.seaofnodes.util.SB;
-
-import java.text.NumberFormat;
-import java.text.ParsePosition;
-
-public class Lexer {
- private final String _srcName; // Source for error messages; usually a file name
- private final byte[] _buf;
- private int _x; // Parser index
- private int _lastNWS; // Index of last non-white-space char
- private final AryInt _lines; // char offset of each line
-
- private final NumberFormat _nf;
- private final ParsePosition _pp;
- private final String _str;
-
- public Lexer(String srcName, String source)
- {
- _srcName = srcName;
- _buf = source.getBytes();
- _x = 0;
-
- // Set fields strictly for Java number parsing
- _nf = NumberFormat.getInstance();
- _nf.setGroupingUsed(false);
- _pp = new ParsePosition(0);
- _str = source; // Keep a complete string copy for java number parsing
- _lines = new AryInt();//
- _lines.push(0); // Line 0 at offset 0
- }
-
- // Skip WS, return true&skip if match, false & do not skip if miss.
- public boolean peek( char c ) { return peek1(skipWS(),c); }
- public boolean peek_noWS( char c ) { return peek1(_x >= _buf.length ? -1 : _buf[_x],c); }
- // Already skipped WS & have character;
- // return true & skip if a match, false& do not skip if a miss.
- public boolean peek1( byte c0, char c ) {
- assert c0==-1 || c0== _buf[_x];
- if( c0!=c ) return false;
- _x++; // Skip peeked character
- return true;
- }
- // Already skipped WS & have character;
- // return true&skip if match, false & do not skip if miss.
- public boolean peek2( byte c0, String s2 ) {
- if( c0 != s2.charAt(0) ) return false;
- if( _x+1 >= _buf.length || _buf[_x+1] != s2.charAt(1) ) return false;
- _x+=2; // Skip peeked characters
- return true;
- }
- public boolean peek( String s ) {
- if( !peek(s.charAt(0)) ) return false;
- if( !peek_noWS(s.charAt(1)) ) { _x--; return false; }
- return true;
- }
- // Peek 'c' and NOT followed by 'no'
- public boolean peek_not( char c, char no ) {
- byte c0 = skipWS();
- if( c0 != c || (_x+1 < _buf.length && _buf[_x+1] == no) ) return false;
- _x++;
- return true;
- }
- // Match any of these, and return the match or null
- public String peek(String[] toks) {
- for( String tok : toks ) if( peek1(tok) ) return tok;
- return null;
- }
- public boolean peek1(String tok) {
- for( int i=0; i<tok.length(); i++ )
- if( _x+i >= _buf.length || _buf[_x+i] != tok.charAt(i) )
- return false;
- return true;
- }
-
-
- /** Advance parse pointer to the first non-whitespace character, and return
- * that character, -1 otherwise. */
- public byte skipWS() {
- int oldx = _x;
- while( _x < _buf.length ) {
- byte c = _buf[_x];
- if( c=='/' && _x+1 < _buf.length && _buf[_x+1]=='/' ) { skipEOL() ; continue; }
- if( c=='/' && _x+1 < _buf.length && _buf[_x+1]=='*' ) { skipBlock(); continue; }
- if( c=='\n' && _x+1 > _lines.last() ) _lines.push(_x+1);
- if( !isWS(c) ) {
- if( oldx-1 > _lastNWS && !isWS(_buf[oldx-1]) ) _lastNWS = oldx-1;
- return c;
- }
- _x++;
- }
- return -1;
- }
- public void skipEOL () { while( _x < _buf.length && _buf[_x] != '\n' ) _x++; }
- public void skipBlock() { throw new UnsupportedOperationException("Block comments not yet implemented"); }
- // Advance parse pointer to next WS. Used for parser syntax error recovery.
- public void skipNonWS() {
- while( _x < _buf.length && !isWS(_buf[_x]) ) _x++;
- }
-
- /** Return true if `c` passes a test */
- public static boolean isWS (byte c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; }
- public static boolean isAlpha0(byte c) { return ('a'<=c && c <= 'z') || ('A'<=c && c <= 'Z') || (c=='_'); }
- public static boolean isAlpha1(byte c) { return isAlpha0(c) || ('0'<=c && c <= '9'); }
- public static boolean isJava (byte c) { return isAlpha1(c) || (c=='$') || (c=='.'); }
- public static boolean isDigit (byte c) { return '0' <= c && c <= '9'; }
-
- public String token() { skipWS(); return token0(); }
- // Lexical tokens. Any alpha, followed by any alphanumerics is a alpha-
- // token; alpha-tokens end with WS or any operator character. Any collection
- // of the classic operator characters are a token, except that they will break
- // un-ambiguously.
- public String token0() {
- if( _x >= _buf.length ) return null;
- byte c=_buf[_x]; int x = _x;
- if( isOp0(c) || (c=='_' && _x+1 < _buf.length && isOp0(_buf[_x+1])) )
- while( _x < _buf.length && isOp1(_buf[_x]) ) _x++;
- else if( isAlpha0(c) )
- while( _x < _buf.length && isAlpha1(_buf[_x]) ) _x++;
- else return null; // Not a token; specifically excludes e.g. all bytes >= 128, or most bytes < 32
- if( (c==':' || c==',') && _x-x==1 ) // Disallow bare ':' as a token; ambiguous with ?: and type annotations; same for ','
- { _x=x; return null; } // Unwind, not a token
- if( c=='-' && _x-x>2 && _buf[x+1]=='>' ) // Disallow leading "->", confusing with function parameter list end; eg "not={x->!x}"
- _x=x+2; // Just return the "->"
- return new String(_buf,x,_x-x);
- }
- public boolean isOp(String s) {
- if( s==null || s.isEmpty() ) return false;
- byte c = (byte)s.charAt(0);
- if( !isOp0(c) && (c!='_' || !isOp0((byte)s.charAt(1))) ) return false;
- for( int i=1; i<s.length(); i++ )
- if( !isOp1((byte)s.charAt(i)) ) return false;
- return true;
- }
-
- // Parse a number; WS already skipped and sitting at a digit. Relies on
- // Javas number parsing.
- public Number number() {
- _pp.setIndex(_x);
- Number n = _nf.parse(_str,_pp);
- _x = _pp.getIndex();
- if( n instanceof Long ) {
- if( _buf[_x-1]=='.' ) _x--; // Java default parser allows "1." as an integer; pushback the '.'
- }
- return n;
- }
-
- public int field_number() {
- byte c = _buf[_x];
- if( !isDigit(c) ) return -1;
- _x++;
- int sum = c-'0';
- while( _x < _buf.length && isDigit(c=_buf[_x]) ) {
- _x++;
- sum = sum*10+c-'0';
- }
- return sum;
- }
-
- /** Parse a String; _x is at '"'.
- * str = [.\%]* // String contents; \t\n\r\% standard escapes
- * str = %[num]?[.num]?fact // Percent escape embeds a 'fact' in a string; "name=%name\n"
- */
- public String string() {
- int oldx = ++_x;
- byte c;
- while( (c=_buf[_x++]) != '"' ) {
- if( c=='%' ) throw new UnsupportedOperationException("Not implemented");
- if( c=='\\' ) throw new UnsupportedOperationException("Not implemented");
- if( _x == _buf.length ) return null;
- }
- return new String(_buf,oldx,_x-oldx-1).intern();
- }
-
- public static boolean isOp0(byte c) { return "!#$%*+-=<>^[]~/&|".indexOf(c) != -1; }
- public static boolean isOp1(byte c) { return isOp0(c) || ":?_{}".indexOf(c) != -1; }
-
- public String errLocMsg(String s) {
- if( s.charAt(0)=='\n' ) return s;
- // find line start
- int a=_x;
- while( a > 0 && _buf[a-1] != '\n' ) --a;
- if( _buf[a]=='\r' ) a++; // do not include leading \n or \n\r
- // find line end
- int b=_x;
- while( b < _buf.length && _buf[b] != '\n' ) b++;
- if( b < _buf.length ) b--; // do not include trailing \n or \n\r
- // Find line number. Bin-search returns the insertion-point, which is the NEXT
- // line unless _x is exactly a line start.
- int line = _lines.binary_search(_x); // Find zero-based line insertion point
- if( line == _lines._len || _lines.at(line)>_x ) line--;
- // error message using 1-based line
- SB sb = new SB().p(_srcName).p(':').p(line+1).p(':').p(s).nl();
- sb.p(new String(_buf,a,b-a)).nl();
- int line_start = a;
- for( int i=line_start; i<_x; i++ )
- sb.p(' ');
- return sb.p('^').nl().toString();
- }
-
- @Override public String toString() { return new String(_buf,_x,_buf.length-_x); }
- @Override public boolean equals(Object loc) {
- if( this==loc ) return true;
- if( !(loc instanceof Lexer p) ) return false;
- return _x==p._x && _srcName.equals(p._srcName);
- }
- @Override public int hashCode() {
- return _srcName.hashCode()+_x;
- }
-}
diff --git a/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/CompilerException.java b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/CompilerException.java
new file mode 100644
index 00000000..c23d3672
--- /dev/null
+++ b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/CompilerException.java
@@ -0,0 +1,28 @@
+/*
+ * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Contributor(s):
+ *
+ * The Initial Developer of the Original Software is
+ * The Ecstasy Project (https://github.com/xtclang/).
+ *
+ * This file is part of the Sea Of Nodes Simple Programming language
+ * implementation. See https://github.com/SeaOfNodes/Simple
+ *
+ * The contents of this file are subject to the terms of the
+ * Apache License Version 2 (the "APL"). You may not use this
+ * file except in compliance with the License. A copy of the
+ * APL may be obtained from:
+ * http://www.apache.org/licenses/LICENSE-2.0
+ */
+package com.seaofnodes.simple.lexer.ecstasy;
+
+public class CompilerException extends RuntimeException {
+ public CompilerException(String message) {
+ super(message);
+ }
+
+ public CompilerException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/common/src/main/java/com/seaofnodes/simple/common/ErrorListener.java b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/ErrorListener.java
index 29ca46b6..6a29f315 100644
--- a/common/src/main/java/com/seaofnodes/simple/common/ErrorListener.java
+++ b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/ErrorListener.java
@@ -1,11 +1,26 @@
-package com.seaofnodes.simple.common;
+/*
+ * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Contributor(s):
+ *
+ * The Initial Developer of the Original Software is
+ * The Ecstasy Project (https://github.com/xtclang/).
+ *
+ * This file is part of the Sea Of Nodes Simple Programming language
+ * implementation. See https://github.com/SeaOfNodes/Simple
+ *
+ * The contents of this file are subject to the terms of the
+ * Apache License Version 2 (the "APL"). You may not use this
+ * file except in compliance with the License. A copy of the
+ * APL may be obtained from:
+ * http://www.apache.org/licenses/LICENSE-2.0
+ */
+package com.seaofnodes.simple.lexer.ecstasy;
import java.text.MessageFormat;
import java.util.Arrays;
-import com.seaofnodes.simple.lexer.ecstasy.Source;
-
import static com.seaofnodes.util.Handy.quotedString;
diff --git a/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Lexer.java b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Lexer.java
index 90571ffd..86a7b77f 100644
--- a/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Lexer.java
+++ b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Lexer.java
@@ -1,4 +1,4 @@
-/**
+/*
* DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Contributor(s):
@@ -28,10 +28,8 @@ import java.util.NoSuchElementException;
import java.util.function.Consumer;
-import com.seaofnodes.simple.common.ErrorListener;
import com.seaofnodes.simple.lexer.ecstasy.Token.Id;
import com.seaofnodes.util.PackedInteger;
-import com.seaofnodes.simple.common.Severity;
import static com.seaofnodes.util.Handy.*;
@@ -1076,6 +1074,17 @@ public class Lexer
{
switch (name)
{
+ case "int":
+ idNum = Id.TYPE_INT;
+ break;
+ case "float":
+ idNum = Id.TYPE_FLOAT;
+ break;
+ case "char":
+ idNum = Id.TYPE_CHAR;
+ break;
+
+ // Following are Ecstasy ids
case "Bit":
idNum = Id.LIT_BIT;
break;
diff --git a/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Severity.java b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Severity.java
new file mode 100644
index 00000000..9b01a7e4
--- /dev/null
+++ b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Severity.java
@@ -0,0 +1,36 @@
+/*
+ * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Contributor(s):
+ *
+ * The Initial Developer of the Original Software is
+ * The Ecstasy Project (https://github.com/xtclang/).
+ *
+ * This file is part of the Sea Of Nodes Simple Programming language
+ * implementation. See https://github.com/SeaOfNodes/Simple
+ *
+ * The contents of this file are subject to the terms of the
+ * Apache License Version 2 (the "APL"). You may not use this
+ * file except in compliance with the License. A copy of the
+ * APL may be obtained from:
+ * http://www.apache.org/licenses/LICENSE-2.0
+ */
+package com.seaofnodes.simple.lexer.ecstasy;
+
+
+/**
+ * Severity levels.
+ */
+public enum Severity
+ {
+ NONE, INFO, WARNING, ERROR, FATAL;
+
+ public String desc()
+ {
+ String sName = name();
+ StringBuilder sb = new StringBuilder(sName.length());
+ sb.append(sName.charAt(0));
+ sb.append(sName.substring(1).toLowerCase());
+ return sb.toString();
+ }
+ }
diff --git a/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Source.java b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Source.java
index 6800138a..c0da5d5f 100644
--- a/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Source.java
+++ b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Source.java
@@ -1,4 +1,4 @@
-/**
+/*
* DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Contributor(s):
diff --git a/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Token.java b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Token.java
index 271d8fbc..18cb5759 100644
--- a/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Token.java
+++ b/common/src/main/java/com/seaofnodes/simple/lexer/ecstasy/Token.java
@@ -1,4 +1,4 @@
-/**
+/*
* DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Contributor(s):
@@ -19,9 +19,6 @@
package com.seaofnodes.simple.lexer.ecstasy;
-import com.seaofnodes.simple.common.ErrorListener;
-import com.seaofnodes.simple.common.Severity;
-
import java.util.HashMap;
import java.util.Map;
@@ -432,6 +429,9 @@ public class Token
case LIT_FLOAT128:
case LIT_FLOATN:
case LIT_BFLOAT16:
+ case TYPE_INT:
+ case TYPE_FLOAT:
+ case TYPE_CHAR:
sb.append(m_id.TEXT)
.append(':')
.append(m_oValue);
@@ -637,6 +637,13 @@ public class Token
IDENTIFIER (null ),
EOL_COMMENT (null ),
ENC_COMMENT (null ),
+
+ // Simple language type keywords
+ TYPE_INT ("int" , false, true),
+ TYPE_FLOAT ("float" , false, true),
+ TYPE_CHAR ("char" , false, true),
+ // End Simple language type keywords
+
LIT_BIT (null ),
LIT_NIBBLE (null ),
LIT_CHAR (null ),
diff --git a/common/src/main/java/com/seaofnodes/util/Handy.java b/common/src/main/java/com/seaofnodes/util/Handy.java
index f3496f8c..62e34ab9 100644
--- a/common/src/main/java/com/seaofnodes/util/Handy.java
+++ b/common/src/main/java/com/seaofnodes/util/Handy.java
@@ -518,7 +518,7 @@ public class Handy
*/
public static boolean isDigit(char ch)
{
- return ch >= '0' & ch <= '9';
+ return ch >= '0' && ch <= '9';
}
/**
@@ -547,7 +547,7 @@ public class Handy
// {..., 0, ..., 9, ..., A, ..., F, ..., a, ..., f, ...}
// so a hexit has to be in the range 0..f, and the entire character
// range from 0..f is only 0x36 (so it fits inside a long mask)
- return ch >= '0' & ch <= 'f' & ((1L << ('f' - ch)) &
+ return ch >= '0' && ch <= 'f' && ((1L << ('f' - ch)) &
// 3 4 5 6
// 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456
// 0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[/]^_`abcdef
@@ -579,7 +579,7 @@ public class Handy
{
// ASCII letters in the unicode range appear in this order:
// {..., A, ..., Z, ..., a, ..., z, ...}
- return ch >= 'A' & ch <= 'z' & ((1L << ('z' - ch)) &
+ return ch >= 'A' && ch <= 'z' && ((1L << ('z' - ch)) &
// 4 5 6 7
// 123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789A
// ABCDEFGHIJKLMNOPQRSTUVWXYZ[/]^_`abcdefghijklmnopqrstuvwxyz
diff --git a/common/src/test/java/com/seaofnodes/simple/lexer/TestLexer.java b/common/src/test/java/com/seaofnodes/simple/lexer/TestLexer.java
index ef29d963..aec5d171 100644
--- a/common/src/test/java/com/seaofnodes/simple/lexer/TestLexer.java
+++ b/common/src/test/java/com/seaofnodes/simple/lexer/TestLexer.java
@@ -15,7 +15,7 @@
package com.seaofnodes.simple.lexer;
-import com.seaofnodes.simple.common.ErrorListener;
+import com.seaofnodes.simple.lexer.ecstasy.ErrorListener;
import com.seaofnodes.simple.lexer.ecstasy.Lexer;
import com.seaofnodes.simple.lexer.ecstasy.Source;
import com.seaofnodes.simple.lexer.ecstasy.Token;