goforth

package module
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 18, 2024 License: MIT Imports: 20 Imported by: 0

README

goforth

This is a compiler and byte code interpreter for a forth-like language.

Static Badge Static Badge Static Badge Static Badge

In general, Forth is, among other things, a programming language whose ideals are freedom and simplicity. Freedom requires knowledge and responsibility, and simplicity is yet more demanding. But "simple" can also mean rudimentary. This Forth attempts to be simple as possible and also fast as possible.

In this forth you can define local variables with curley brackets like in other forths and produce callable byte code subroutines. The compiler produces human readable bytecode and is amazingly fast.

Overview

goforth is the interactive compiler which produces a sequence of commands for a virtual stack machine also known as "Forth machine". The code is then interpreted and executed.

Why goforth?

  • Goforth can be used as an embeddable programming language. See topic embedding.
  • Goforth can be used as a compiler to produce C-code from forth. See topic Generate C-code.
  • Goforth can be embedded in all sorts of different documents using the template sytax. See topic templating.

Currently there is no parallel ForthVM execution via goroutines.

Installation

If you do want to get the latest version of goforth, change to any directory that is both outside of your GOPATH and outside of a module (a temp directory is fine), and run:

go install github.com/loscoala/goforth/cmd/goforth@latest

If you do want to add the latest version to your go.mod inside your project run:

go get github.com/loscoala/goforth@latest

Build and Dependencies

  • All you need is a golang compiler 1.22

  • Build the project with build.sh or go build -C cmd/goforth

  • For C code generation you need a working C compiler.

git clone https://github.com/loscoala/goforth.git
./build.sh

Usage

Execute goforth. See "Installation"

  1. You can also execute forth-scripts:
goforth --file=forthscript.fs
echo ": main 5 5 + . ;" | goforth
  1. In the Shebang you can alternatively write the following:
#!goforth --file
# code goes here...
  1. Or in the command line:
goforth -script '
: myfunc ." Hello World" ;
: main myfunc ;
'

The compiler has core.fs automatically included into the binary.

Howto start

Start ./goforth and in the REPL you can look what the dictionary contains by typing:

command description
% Shows the complete dictionary
% name Shows the definition of name in the dictionary
use filename Loads a file
$ Shows the values of the stack

Examples

Global variables

Globals are defined with the variable compiler-builtin keyword.

Lets define a variable xx. This defines an entry in the global dictionary.

forth> variable xx

Now you can set for example the value 15 to xx:

forth> 15 to xx
forth> xx .  \ prints the value of variable xx

Or lets sum all values from 0 to 100:

forth> variable vv
forth> 101 0 [ vv + to vv ] for vv .
Mandelbrot

Start the interpreter:

./cmd/goforth/goforth

Then inside the REPL type the following:

forth> use examples/mandelbrot.fs \ loads the file mandelbrot.fs and parses it
forth> mb-init                    \ compile and run mb-init
forth> true debug                 \ OPTIONAL in order to show byte code and benchmark
forth> mb                         \ compile and run mb

As a result you see a zoom in the mandelbrot fractal.

mandelbrot-video

Interactive development

You can also define new words:

forth> : myadd 2 + ;

Or words with a local context:

: fakulty
  1 { x }
  1+ 1 ?do
    x i * to x
  loop
  x
;

then run it:

forth> 5 fakulty .

which prints:

120
Generate C-Code

To translate one or more words into C code and generate a native binary file there is a compile statement.

This is how the html.fs example written in Forth can be easily translated into C and executed immediately:

forth> use examples/html.fs
forth> compile test

The word test was defined in the sample file as follows:

: test
  document
  [
    [
      [ ." charset=\"utf-8\"" ] meta
      [ ." Example Page" ] title
    ] head
    [
      [ ." Example Page" ] h1
      [ ." Hello " [ ." World!" ] b ] p
    ] body
  ] html
;

After the compile test statement, a C file main.c was generated in the lib directory and compiled with the C compiler and executed.

The result is also shown as follows:

<!doctype html>
<html lang="de-DE"><head><meta charset="utf-8"><title>Example Page</title></head><body><h1>Example Page</h1><p>Hello <b>World!</b></p></body></html>
Call external programs

The following example shows the usage of the sh word to list all the files and directories in the current directory under linux machine.

: ls
  100 [
    [ s" ls -l" ] sh
  ] alloc
;

Optional: Now you can compile the word to a native binary.

forth> compile ls
Debugging

By calling true debug you can enable the benchmark mode.

forth> true debug

Now the byte code is displayed and the execution time of the program.

forth> true debug
forth> 5 3 min .
SUB min;TDP;LSI;JIN #0;DRP;JMP #1;#0 NOP;SWP;DRP;#1 NOP;END;
MAIN;L 5;L 3;CALL min;PRI;STP;
3

execution time: 15.947µs
Number of Cmds: 13
Speed: 0.000815 cmd/ns

The actual debugger can be run like this:

forth> debug 34 21 min .

Which gives the following result:

SUB min;TDP;LSI;JIN #0;DRP;JMP #1;#0 NOP;SWP;DRP;#1 NOP;END;
MAIN;L 34;L 21;CALL min;PRI;STP;

 11 MAIN            |                           |                           |
 12 L 34            | 34                        |                           |
 13 L 21            | 34 21                     |                           |
 14 CALL min        | 34 21                     | 14                        |
  1 TDP             | 34 21 34 21               | 14                        |
  2 LSI             | 34 21 0                   | 14                        |
  3 JIN #0          | 34 21                     | 14                        |
  6 NOP #0          | 34 21                     | 14                        |
  7 SWP             | 21 34                     | 14                        |
  8 DRP             | 21                        | 14                        |
  9 NOP #1          | 21                        | 14                        |
 10 END             | 21                        |                           |
 15 PRI             |                           |                           | 21
 16 STP

As you can see on the top there is the ByteCode and below you see the program pointer, the command, the stack, the return stack and the output.

Embedding

First, you have to add goforth to go.mod:

go get github.com/loscoala/goforth@latest

Then in golang you can import goforth:

import "github.com/loscoala/goforth"

Now all you need is a ForthCompiler:

fc := goforth.NewForthCompiler()

fc.Fvm.Sysfunc = func(fvm *goforth.ForthVM, syscall int64) {
  switch syscall {
  case 999:
    value := fvm.Pop()
    fvm.Push(value + value)
    fmt.Println("This is a custom sys call in Forth")
  default:
    fmt.Println("Not implemented")
  }
}

// Parse the Core lib:
if err := fc.Parse(goforth.Core); err != nil {
  goforth.PrintError(err)
}

// Run some code:
if err := fc.Run(": main .\" Hello World!\" ;"); err != nil {
  goforth.PrintError(err)
}

// Call custom syscall (calculated 10+10 and prints it):
if err := fc.Run(": customcall 999 sys ; : main 10 customcall . ;"); err != nil {
  goforth.PrintError(err)
}

Templates

Goforth has a meta command called template <name> <filename>. When goforth parses a template file, it looks for opening and closing tags, which are <?fs and ?> which tell goforth to start and stop embedding the code between them. Parsing in this manner allows goforth to be embedded in all sorts of different documents, as everything outside of a pair of opening and closing tags is ignored by the goforth parser.

Example #1 Goforth opening and closing tags

Example file html.tfs:

<html>
  <head>
    <title><?fs title .s ?></title>
  </head>
  <body>
<?fs 10 0 do ?>
    <p><?fs i . ?></p>
<?fs loop ?>
  </body>
</html>

Now you can load the template named test:

forth> template test html.tfs
forth> variable title
forth> a" Example title" to title
forth> test

Description of the files

filename description
main.go The main function
compiler.go The forth compiler
vm.go A stack machine with additional functionality for forth
stack.go A stack of strings implementation
label.go A label generator for the bytecode
show.go Visual representation and the REPL
config.go Configuration during runtime

VM commands

The stack machine consists of a single LIFO stack and memory. Memory is adressed by an unsigned number. The stack and memory consists only of numbers.

command description
RDI Reads a value from the input. The input is implementation dependant.
PRI Prints a value from the stack as a number.
PRA Prints a value from the stack as an character.
DUP Duplicates the top stack value.
OVR Copies the second value from the top on the top.
TVR Copies the second pair onto the top pair.
TWP Swaps the top pair with the pair under it.
QDP ?dup implemented as: if the top value is zero - duplicate it - otherwise do nothing.
ROT Rotates the top three elements by pushing to stack one element "down" an taking the last element on the top.
TDP Duplicates the top pair on the stack.
DRP Drops the top element on the stack.
SWP Swaps the top pair on the stack.
#id NOP Does nothing. Used for labeling.
JMP #id Unconditional jump to label with id
JIN #id Conditional jump to label with id if the top value on the stack is zero.
ADI Simple addition of the top pair.
SBI Simple subtraction of the top pair.
DVI Simple division of the top pair.
LSI 1 on the stack if the top element is less than the second element. 0 otherwise.
GRI 1 on the stack if the top element id greater than the second element. 0 otherwise.
MLI Multiplies the first two elements on the stack.
ADF Simple float addition of the top pair.
SBF Simple float subtraction of the top pair.
MLF Multiplies the first two float elements on the stack.
DVF Simple float division of the top pair.
PRF Prints a float value from the stack as a number.
LSF 1 on the stack if the top float element is less than the second element. 0 otherwise.
GRF 1 on the stack if the top float element is greater than the second element. 0 otherwise.
OR 1 if one of the first two elements on the stack unequal to 0 else 0.
AND 1 if the first two elements on the stack unequal to 0 else 0.
NOT 1 if the first element on the stack is 0 else 0.
EQI 1 if the first two element on the stack are equal else 0.
LV Loads a value from the given address.
L number Loads a value on the stack.
LF float Loads a float on the stack.
STR Stores the second element from the stack into the memory address which is the first element from the stack.
SYS Make a syscall. The top element from the stack is used to make different syscalls.
STP Quits the execution.
SUB name Declares a subroutine. Used only for code generation. Not used by the vm
END Ends the subroutine. Used only for code generation. Not used by the vm
MAIN Declares the beginning of the main. Used only for code generation. Not used by the vm
GDEF Creates a new global variable initialized with zero
GSET Assigns the top value of the stack to a global variable
GBL Pushes the global value on top of the stack
LCTX Creates a new context of local variables
LDEF Pops the top value of the stack and copies it to the local definitions
LSET Assigns the top value of the stack to a local variable
LCL Pushes the local value on top of the stack
LCLR Clears the local definitions
CALL name Call a SUB routine.
REF name Pushes the address of a SUB on top of the stack
EXC Pops the top value from the stack and calls a SUB routine.
PCK dup = 0 pick
NRT -rot = rot rot
TR to r
FR from r
RF r fetch
TTR 2 to r
TFR 2 from r
TRF 2 r fetch
INC Increments the top value of the stack by 1

Documentation

Index

Constants

View Source
const (
	RDI = iota
	PRI
	PRA
	DUP
	OVR
	TVR
	TWP
	QDP
	ROT
	TDP
	DRP
	SWP
	NOP
	JMP
	JIN
	ADI
	SBI
	DVI
	LSI
	GRI
	MLI
	ADF
	SBF
	MLF
	DVF
	PRF
	LSF
	GRF
	OR
	AND
	NOT
	EQI
	XOR
	LV
	L
	LF
	STR
	SYS
	STP
	SUB
	END
	MAIN
	GDEF
	GSET
	GBL
	LCTX
	LDEF
	LSET
	LCL
	LCLR
	CALL
	REF
	EXC
	PCK
	NRT
	TR  // to r
	FR  // from r
	RF  // r fetch
	TTR // 2 to r
	TFR // 2 from r
	TRF // 2 r fetch
	INC
)

Variables

View Source
var (
	Magenta = color.New(color.FgHiMagenta).SprintFunc()
	Cyan    = color.New(color.FgHiCyan).SprintFunc()
	Green   = color.New(color.FgHiGreen).SprintFunc()
	Blue    = color.New(color.FgHiBlue).SprintFunc()
	Yellow  = color.New(color.FgHiYellow).SprintFunc()
	Red     = color.New(color.FgHiRed).SprintFunc()
)
View Source
var CAutoCompile = true

Compile automatically after C code generation

View Source
var CAutoExecute = true

Automatically execute the binary after compiling

View Source
var CBinaryName = "main"

The name of the binary

View Source
var CCodeName = "main.c"

The name of the C code file

View Source
var CCompiler = "cc"

The name of the C compiler

View Source
var COptimization = "-O2"

The optimization flag of the C compiler

View Source
var CellName = map[int]string{
	RDI:  "RDI",
	PRI:  "PRI",
	PRA:  "PRA",
	DUP:  "DUP",
	OVR:  "OVR",
	TVR:  "TVR",
	TWP:  "TWP",
	QDP:  "QDP",
	ROT:  "ROT",
	TDP:  "TDP",
	DRP:  "DRP",
	SWP:  "SWP",
	NOP:  "NOP",
	JMP:  "JMP",
	JIN:  "JIN",
	ADI:  "ADI",
	SBI:  "SBI",
	DVI:  "DVI",
	LSI:  "LSI",
	GRI:  "GRI",
	MLI:  "MLI",
	ADF:  "ADF",
	SBF:  "SBF",
	MLF:  "MLF",
	DVF:  "DVF",
	PRF:  "PRF",
	LSF:  "LSF",
	GRF:  "GRF",
	OR:   "OR",
	AND:  "AND",
	NOT:  "NOT",
	EQI:  "EQI",
	XOR:  "XOR",
	LV:   "LV",
	L:    "L",
	LF:   "LF",
	STR:  "STR",
	SYS:  "SYS",
	STP:  "STP",
	SUB:  "SUB",
	END:  "END",
	MAIN: "MAIN",
	GDEF: "GDEF",
	GSET: "GSET",
	GBL:  "GBL",
	LCTX: "LCTX",
	LDEF: "LDEF",
	LSET: "LSET",
	LCL:  "LCL",
	LCLR: "LCLR",
	CALL: "CALL",
	REF:  "REF",
	EXC:  "EXC",
	PCK:  "PCK",
	NRT:  "NRT",
	TR:   "TR",
	FR:   "FR",
	RF:   "RF",
	TTR:  "TTR",
	TFR:  "TFR",
	TRF:  "TRF",
	INC:  "INC",
}
View Source
var Colored bool

Colored output

View Source
var Core string

The core words dictionary

View Source
var Repl = "forth> "

The prompt in StartREPL

View Source
var ShowByteCode bool

Show byte code in StartREPL

View Source
var ShowExecutionTime bool

Show execution time in vm.Run

Functions

func PrintError

func PrintError(err error)

func PrintWarning added in v0.5.0

func PrintWarning(warning string)

Types

type Cell

type Cell struct {
	// contains filtered or unexported fields
}

func (Cell) String

func (c Cell) String() string

type Code

type Code struct {
	PosMain int   // position of MAIN
	ProgPtr int   // program pointer, used in RunStep
	Command *Cell // current command to execute, used in RunStep
	// contains filtered or unexported fields
}

type Compiler

type Compiler interface {
	Compile() error
	Parse(s string) error
	ParseFile(filename string) error
	StartREPL()
	RunFile(filename string) error
	Run(prog string) error
}

This is the public API for ForthCompiler.

type ForthCompiler

type ForthCompiler struct {
	Fvm *ForthVM
	// contains filtered or unexported fields
}

func NewForthCompiler

func NewForthCompiler() *ForthCompiler

func (*ForthCompiler) ByteCode

func (fc *ForthCompiler) ByteCode() string

The ByteCode of the "main" word previously compiled with Compile().

func (*ForthCompiler) Compile

func (fc *ForthCompiler) Compile() error

Compiles the word "main". Side effect: ByteCode() contains the result.

func (*ForthCompiler) CompileFile added in v0.2.0

func (fc *ForthCompiler) CompileFile(str string) error

func (*ForthCompiler) CompileScript added in v0.2.0

func (fc *ForthCompiler) CompileScript(script string) error

func (*ForthCompiler) CompileToC added in v0.2.0

func (fc *ForthCompiler) CompileToC() error

func (*ForthCompiler) Parse

func (fc *ForthCompiler) Parse(str string) error

Parses the given Forth code and adds the word to the dictionary of the compiler.

func (*ForthCompiler) ParseFile

func (fc *ForthCompiler) ParseFile(filename string) error

func (*ForthCompiler) ParseTemplate added in v0.5.0

func (fc *ForthCompiler) ParseTemplate(entry, str string) error

func (*ForthCompiler) ParseTemplateFile added in v0.5.0

func (fc *ForthCompiler) ParseTemplateFile(entry, filename string) error

func (*ForthCompiler) ReadFile added in v0.5.0

func (fc *ForthCompiler) ReadFile(filename string) ([]byte, error)

func (*ForthCompiler) Run

func (fc *ForthCompiler) Run(prog string) error

func (*ForthCompiler) RunFile

func (fc *ForthCompiler) RunFile(str string) error

func (*ForthCompiler) StartREPL

func (fc *ForthCompiler) StartREPL()

type ForthVM

type ForthVM struct {
	Vars   map[string]int64
	Mem    []int64
	Stack  []int64
	Rstack []int64

	Sysfunc  func(*ForthVM, int64)
	Out      io.Writer
	CodeData *Code
	// contains filtered or unexported fields
}

func NewForthVM

func NewForthVM() *ForthVM

func (*ForthVM) Adf

func (fvm *ForthVM) Adf()

f+

func (*ForthVM) Adi

func (fvm *ForthVM) Adi()

func (*ForthVM) And

func (fvm *ForthVM) And()

func (*ForthVM) Drp

func (fvm *ForthVM) Drp()

func (*ForthVM) Dup

func (fvm *ForthVM) Dup()

func (*ForthVM) Dvf

func (fvm *ForthVM) Dvf()

f/

func (*ForthVM) Dvi

func (fvm *ForthVM) Dvi()

func (*ForthVM) Eqi

func (fvm *ForthVM) Eqi()

func (*ForthVM) Fpop

func (fvm *ForthVM) Fpop() float64

func (*ForthVM) Fpush

func (fvm *ForthVM) Fpush(f float64)

func (*ForthVM) Fr

func (fvm *ForthVM) Fr()

func (*ForthVM) Gbl

func (fvm *ForthVM) Gbl(name string)

func (*ForthVM) Gdef

func (fvm *ForthVM) Gdef(name string)

func (*ForthVM) GetString

func (fvm *ForthVM) GetString() string

func (*ForthVM) Grf

func (fvm *ForthVM) Grf()

f>

func (*ForthVM) Gri

func (fvm *ForthVM) Gri()

func (*ForthVM) Gset

func (fvm *ForthVM) Gset(name string)

func (*ForthVM) Inc added in v0.5.0

func (fvm *ForthVM) Inc()

func (*ForthVM) Jin

func (fvm *ForthVM) Jin() bool

func (*ForthVM) Lcl

func (fvm *ForthVM) Lcl(name int)

func (*ForthVM) Lclr

func (fvm *ForthVM) Lclr()

func (*ForthVM) Lctx

func (fvm *ForthVM) Lctx()

func (*ForthVM) Ldef

func (fvm *ForthVM) Ldef(name int)

func (*ForthVM) Lset

func (fvm *ForthVM) Lset(name int)

func (*ForthVM) Lsf

func (fvm *ForthVM) Lsf()

f<

func (*ForthVM) Lsi

func (fvm *ForthVM) Lsi()

func (*ForthVM) Lv

func (fvm *ForthVM) Lv()

func (*ForthVM) Mlf

func (fvm *ForthVM) Mlf()

f*

func (*ForthVM) Mli

func (fvm *ForthVM) Mli()

func (*ForthVM) Not

func (fvm *ForthVM) Not()

func (*ForthVM) Nrot

func (fvm *ForthVM) Nrot()

func (*ForthVM) Or

func (fvm *ForthVM) Or()

func (*ForthVM) Ovr

func (fvm *ForthVM) Ovr()

func (*ForthVM) Pick

func (fvm *ForthVM) Pick()

func (*ForthVM) Pop

func (fvm *ForthVM) Pop() int64

func (*ForthVM) Pra

func (fvm *ForthVM) Pra()

func (*ForthVM) PrepareRun

func (fvm *ForthVM) PrepareRun(codeStr string)

Initializes the virtual machine. You should call the method before RunStep.

func (*ForthVM) Prf

func (fvm *ForthVM) Prf()

f.

func (*ForthVM) Pri

func (fvm *ForthVM) Pri()

func (*ForthVM) Push

func (fvm *ForthVM) Push(i int64)

func (*ForthVM) Qdp

func (fvm *ForthVM) Qdp()

func (*ForthVM) Rdi

func (fvm *ForthVM) Rdi()

func (*ForthVM) Rf

func (fvm *ForthVM) Rf()

func (*ForthVM) Rot

func (fvm *ForthVM) Rot()

func (*ForthVM) Rpop

func (fvm *ForthVM) Rpop() int64

func (*ForthVM) Rpush

func (fvm *ForthVM) Rpush(i int64)

func (*ForthVM) Run

func (fvm *ForthVM) Run(codeStr string)

Executes the ByteCode passed as a parameter.

func (*ForthVM) RunStep

func (fvm *ForthVM) RunStep() (bool, error)

Runs a single step of the virtual machine. Note: You must call PrepareRun once before calling RunStep for the first time

func (*ForthVM) Sbf

func (fvm *ForthVM) Sbf()

f-

func (*ForthVM) Sbi

func (fvm *ForthVM) Sbi()

func (*ForthVM) SetString

func (fvm *ForthVM) SetString(str string)

func (*ForthVM) Str

func (fvm *ForthVM) Str()

func (*ForthVM) Swp

func (fvm *ForthVM) Swp()

func (*ForthVM) Sys

func (fvm *ForthVM) Sys()

func (*ForthVM) Tdp

func (fvm *ForthVM) Tdp()

func (*ForthVM) Tfr

func (fvm *ForthVM) Tfr()

func (*ForthVM) Tr

func (fvm *ForthVM) Tr()

func (*ForthVM) Trf

func (fvm *ForthVM) Trf()

func (*ForthVM) Ttr

func (fvm *ForthVM) Ttr()

func (*ForthVM) Tvr

func (fvm *ForthVM) Tvr()

func (*ForthVM) Twp

func (fvm *ForthVM) Twp()

func (*ForthVM) Xor

func (fvm *ForthVM) Xor()

type Label

type Label struct {
	// contains filtered or unexported fields
}

func (*Label) CreateNewLabel

func (l *Label) CreateNewLabel() string

func (*Label) CreateNewWord

func (l *Label) CreateNewWord() string

type Local

type Local struct {
	// contains filtered or unexported fields
}

type SliceStack

type SliceStack[T comparable] []*Stack[T]

func (*SliceStack[T]) Contains

func (ss *SliceStack[T]) Contains(val T) bool

func (*SliceStack[T]) ExPop

func (ss *SliceStack[T]) ExPop() *Stack[T]

func (*SliceStack[T]) IsEmpty

func (ss *SliceStack[T]) IsEmpty() bool

func (*SliceStack[T]) Len

func (ss *SliceStack[T]) Len() int

func (*SliceStack[T]) Pop

func (ss *SliceStack[T]) Pop() (*Stack[T], bool)

func (*SliceStack[T]) Push

func (ss *SliceStack[T]) Push(stk *Stack[T])

type Stack

type Stack[T comparable] struct {
	// contains filtered or unexported fields
}

func NewStack

func NewStack[T comparable]() *Stack[T]

func (*Stack[T]) Contains

func (s *Stack[T]) Contains(val T) bool

func (*Stack[T]) Each

func (s *Stack[T]) Each(f func(value T))

func (*Stack[T]) ExFetch

func (s *Stack[T]) ExFetch() T

func (*Stack[T]) ExPop

func (s *Stack[T]) ExPop() T

func (*Stack[T]) Fetch

func (s *Stack[T]) Fetch() (T, bool)

func (*Stack[T]) GetIndex

func (s *Stack[T]) GetIndex(val T) int

func (*Stack[T]) IsEmpty

func (s *Stack[T]) IsEmpty() bool

func (*Stack[T]) Iter

func (s *Stack[T]) Iter() *StackIter[T]

func (*Stack[T]) Len

func (s *Stack[T]) Len() int

func (*Stack[T]) Pop

func (s *Stack[T]) Pop() (T, bool)

func (*Stack[T]) Push

func (s *Stack[T]) Push(val T)

func (*Stack[T]) Reverse

func (s *Stack[T]) Reverse() *Stack[T]

type StackIter

type StackIter[T comparable] struct {
	// contains filtered or unexported fields
}

func (*StackIter[T]) Get

func (s *StackIter[T]) Get() T

func (*StackIter[T]) Next

func (s *StackIter[T]) Next() bool

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL